0

After trying to identify the problem with my generic method, I've ended up with the following code:

        Map<String, BiConsumerThatThrows<CheckPayment, XMLEventReader>> a = CheckPayment.childMapper;
        BiConsumerThatThrows<CheckPayment, XMLEventReader> a1 = a.get("1");
        BiConsumerThatThrows<? super CheckPayment, XMLEventReader> b1 = a1;
        Map<String, BiConsumerThatThrows<? super CheckPayment, XMLEventReader>> b = new HashMap<>();
        b.put("1", b1);
        b = a;

It won't compile with the following error (I've formatted it to make it more readable):

[ERROR] /D:/lalala/MyClass.java:[152,27] 
    incompatible types: 
        java.util.Map<
            java.lang.String,
            blablabla.BiConsumerThatThrows<
                blablabla.CheckPayment,
                javax.xml.stream.XMLEventReader
            >
        > 
    cannot be converted to 
        java.util.Map<
            java.lang.String,
            blablabla.BiConsumerThatThrows<
                ? super blablabla.CheckPayment,
                javax.xml.stream.XMLEventReader
            >
        >

What's most surprising, it crashes only on the last line of the given code snippet.

Why does it happen? What can be done to perform such assignment?

I'm using Oracle JDK 1.8u40 x64.

P.S. Here is the simplified example:

Set<Set<String>> sets = new HashSet<>();
Set<Set<? super String>> sets2 = new HashSet<>();
sets2 = sets;

Error:

incompatible types: java.util.Set<java.util.Set<java.lang.String>> cannot be converted to java.util.Set<java.util.Set<? super java.lang.String>>

P.P.S. Even this is not working:

Set<Set<Object>> sets2 = new HashSet<Set<String>>();

why?

i.y.
  • 153
  • 11
  • I'd suggest that you refactor your code and regenerate your problem with a less complicated structures and names, so that it would be easier to understand your problem. – Ofek Ron Oct 21 '15 at 11:57
  • have you specified the equals() function? i suggest you read this: http://stackoverflow.com/questions/1628718/java-type-safety-generics-equals – cristianhh Oct 21 '15 at 11:58
  • You're getting types incompatible error because you're assigning incompatible types... – Bob Oct 21 '15 at 11:59
  • @OfekRon, it won't get any simpler. Basically, it's just type inference in a generic argument. – i.y. Oct 21 '15 at 12:02
  • @cristianhh, key is of a type java.lang.String. Where do I need equals(...)? – i.y. Oct 21 '15 at 12:02
  • @lan, isn't X instance of (? super X)? – i.y. Oct 21 '15 at 12:03
  • @Manoj, this line compiles successfully. The problem is in the last line (b = a). – i.y. Oct 21 '15 at 12:04
  • @i.y. yeah, you are right. Please go through my answer. – Manoj Oct 21 '15 at 12:28

1 Answers1

3

The issue is that generic type parameter constraints are not transparent for nested constraints. A Map<String, List<Integer>> is a different type to Map<String, List<? super Integer>>, and you can't assign one to the other. This is the issue you're coming across here.

To fix it, you need to make the wildcard apply all the way out to the top-level generic declaration with extends:

Map<String, ? extends List<? super Integer>> m = new HashMap<String, List<Integer>>()

Note that this is an extends, not a super because a List<Integer> is a subtype of a List<? super Integer> (I find this mindbending as well). So in your case this would be:

Map<String, ? extends BiConsumerThatThrows<? super CheckPayment, XMLEventReader>> b = ...

and the simplified example:

Set<? extends Set<? super String>> sets2 = new HashSet<>();

This might help with understanding this: http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ104

thecoop
  • 45,220
  • 19
  • 132
  • 189
  • Thanks, looks like that's the case. However, it looks like this is not a problem for functional interfaces: `Function> childMappers = (s) -> { if (CheckPayment.childMapper.containsKey(s)) return CheckPayment.childMapper.get(s); else if (AbstractOperation.childMapper.containsKey(s)) return AbstractOperation.childMapper.get(s); else return null; };` – i.y. Oct 21 '15 at 12:52