Bizarre clashes with the capture of generics

Please give me a hint about what is going on here:

List<? extends Number> a = new ArrayList<Number>(); List<? extends Number> b = new ArrayList<Number>(); a.addAll(b); // ouch! compiler yells at me, see the block below: /* incompatible types found : java.util.List<capture#714 of ? extends java.lang.Number> required: java.util.List<java.lang.Number> */ 

This simple code does not compile. I vaguely remember something related to type captures, for example, those that should be mainly used in the interface specifications, and not in the code itself, but I didn’t get fooled that way.

This, of course, can be fixed roughly, like this:

 List<? extends Number> a = new ArrayList<Number>(); List<? extends Number> b = new ArrayList<Number>(); @SuppressWarnings({"unchecked"}) List<Number> aPlain = (List<Number>) a; @SuppressWarnings({"unchecked"}) List<Number> bPlain = (List<Number>) b; aPlain.addAll(bPlain); 

So, do I really need to either abandon the captures in the ad (the capture came to me from the interface, so I have to change some kind of API), or stick to sizes with suppression annotations (which usually suck and complicate the code a bit)?

+6
source share
5 answers

You have essentially two lists of possible types. Because ? extends Number ? extends Number means a class that extends Number . Therefore, for list a it can be classA , and for list b it can be, for example, classB . They are incompatible, they can be completely different.

+9
source

The problem is that if you use List<? extends Number> List<? extends Number> , you could really:

 List<? extends Number> a = new ArrayList<Integer>(); List<? extends Number> b = new ArrayList<Double>(); a.addAll(b); //ouch, would add Doubles to an Integer list 

The compiler cannot determine from List<? extends Number> List<? extends Number> , which is the actual type parameter and therefore will not allow you to perform the add operation.

You also should not pass lists to List<Number> if you get them as a parameter, since you really can have a list of Integer objects and add double objects to it.

In this case, you better create a new List<Number> and add objects from both lists:

 List<Number> c = new ArrayList<Number>(a.size() + b.size()); c.addAll(a); c.addAll(b); 

Edit: in case you create both lists locally, you still won't substitute the wildcard ? (since you will always have a List<Number> ).

+6
source

Do not use a wildcard ? . This means “Some specific types that I don’t know,” and since you don’t know the type, you cannot add anything to the list. Change your code to use List<Number> and everything will work.

This is quickly becoming the most frequently asked Java question, in a hundred options ...

+3
source

PECS (producer-expands, consumer-super)

  • You cannot put anything in a type declared using the EXTENDS template except for the null value that applies to each reference type
  • You cannot get any of the type declared with SUPER wildcard, except for the value of the Object type, which is the super-type of each reference type
+2
source

The fact is that

 List<? extends Number> a = new ArrayList<Number>(); List<? extends Number> b = new ArrayList<Number>(); 

can also be read as:

 List<x extends Number> a = new ArrayList<Number>(); List<y extends Number> b = new ArrayList<Number>(); 

How does the compiler know that x and y are the same?

+1
source

Source: https://habr.com/ru/post/887120/


All Articles