Does the Java List behave like a covariant type during initialization?

I know that lists in Java are invariant.

So the second statement below gives a compilation error as expected

List<Integer> integers = Arrays.asList(1, 2, 3); List<Number> numbers = integers; 

However, all of these features work great.

  List<Integer> numbers1 = Arrays.asList(1, 2, 3); List<? extends Number> numbers2 = Arrays.asList(1, 2, 3); List<Number> numbers3 = Arrays.asList(1, 2, 3); 

So my question is, how is compiling the last statement above?

I understand that Arrays.asList() takes a type from its caller, but I thought Arrays.asList(1,2,3) resolves the closest List<Integer> and setting it to List<Number> will not compile since lists are invariant.

What am I missing?

+5
source share
4 answers

There is no covariance. Instead, the Java compiler uses the context of the call to Arrays.asList<T> to output the type T that your program wanted to specify to call the method.

Prior to Java 8, you can explicitly specify a type, e.g.

 List<Integer> numbers1 = Arrays.<Integer>asList(1, 2, 3); List<? extends Number> numbers2 = Arrays.<? extends Number>asList(1, 2, 3); List<Number> numbers3 = Arrays.<Number>asList(1, 2, 3); 

Noting how the above code snippets repeat type T on both sides of the destination, the Java compiler sets output rules for spreading T from the left side of the job to the right side, eliminating the repetition.

Link: Type selection tutorial .

+4
source

JLS illustrates this case well.
This is not covariance during initialization, as it will also work in "classic" method calls.
Note that the output strategy used in your example makes it work in Java 8, but it does not work in Java 7.

18.5.2. Conclusion type inference

Consider the example from the previous section:

List<Number> ln = Arrays.asList(1, 2.0);

The most specific applicable method has been identified as:

public static <T> List<T> asList(T... a)

In order to complete the type checking of a method call, we must determine whether it is compatible with the target type List<Number> .

The related set used to demonstrate applicability in previous section B2 was:

{ α <: Object, Integer <: α, Double <: α }

A new set of constraint formulas is as follows:

{ ‹List<α> → List<Number>› }

This compatibility constraint gives an equality estimate for α, which is part of a new connected collection, B3:

{ α <: Object, Integer <: α, Double <: α, α = Number }

These boundaries are trivially allowed:

α = Number

Finally, we perform a substitution on the declared return type asList to determine that the method call is of type List; Obviously, this is compatible with the type of target.

This output strategy is different from Java SE 7 Edition. The Java® language specification, which would have to create an instance of α based on its lower bounds (before even considering the purpose of the type call), as it was in the previous section. This will result in a type error, since the resulting type is not a subtype of List.

+1
source

B / c Number is the superclass of these subclasses:

Direct known subclasses: AtomicInteger, AtomicLong, BigDecimal, BigInteger, Byte, Double, DoubleAccumulator, DoubleAdder, Float, Integer, Long, LongAccumulator, LongAdder, Short

Source: https://docs.oracle.com/javase/9/docs/api/java/lang/Number.html

In your first code snippet, you add “fruits” to the “apples” basket (you cannot do this!), In the second case this is not true.

0
source

Of course, Arrays.asList(1, 2, 3) gives a List<Integer> . Then List<Integer> is a valid List<? extends Number> List<? extends Number> . You can prove it to yourself:

 List<? extends Number> l1 = new ArrayList<>(); List<Integer> l2 = new ArrayList<>(); l1 = l2; 

However, on the contrary, this is not so:

 l2 = l1; // does not compile 

Integer is a subclass of Number , so List<Integer> is a valid List things that extend Number .

0
source

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


All Articles