Reputation: 9230
Given this Java code:
import java.util.AbstractMap.SimpleEntry;
import java.util.Arrays;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
public class Test {
public static void main(String[] args) {
SimpleEntry<Integer, String> simpleEntry = new SimpleEntry<>(1, "1");
Optional<Entry<Integer, String>> optionalEntry = Optional.of(simpleEntry);
Optional<SimpleEntry<Integer, String>> optionalSimpleEntry = Optional.of(simpleEntry);
List<Entry<Integer, String>> list1 = Arrays.asList(simpleEntry);
List<Optional<Entry<Integer, String>>> list2 = Arrays.asList(optionalEntry);
List<Optional<SimpleEntry<Integer, String>>> list3 = Arrays.asList(optionalSimpleEntry);
List<Optional<Entry<Integer, String>>> list4 = Arrays.asList(optionalSimpleEntry);
}
}
The expressions initializing list
, list2
and list3
work fine. However, the expression initializing list4
breaks with this error in Eclipse:
Type mismatch: cannot convert from List<Optional<AbstractMap.SimpleEntry<Integer,String>>>
to List<Optional<Map.Entry<Integer,String>>>
and this error in javac
:
Test.java:16: error: incompatible types: inference variable T has incompatible bounds
List<Optional<Entry<Integer, String>>> list4 = Arrays.asList(optionalSimpleEntry);
^
equality constraints: Optional<Entry<Integer,String>>
lower bounds: Optional<SimpleEntry<Integer,String>>
where T is a type-variable:
T extends Object declared in method <T>asList(T...)
But AbstractMap.SimpleEntry
directly implements Map.Entry
. So why does type inference break for list4
when it works for list1
to list3
(and also for the assignment to optionalEntry
, for that matter)?
Particularly I don't understand why the assignment to list1
works when the assignment to list4
does not.
Upvotes: 5
Views: 159
Reputation: 31299
I assume you understand why Optional<SimpleEntry<Integer,String>>
cannot be assigned to a variable of type List<Optional<Entry<Integer, String>>>
. If not, please read the Q&A Is List a subclass of List? Why are Java generics not implicitly polymorphic?
However, your question why the list1
declaration works but the list4
declaration.
There is a difference between the list1
and list4
declarations. For list1
, the form is:
SimpleEntry<Integer, String> simpleEntry = ...;
List<Entry<Integer, String>> list = Arrays.asList<T>(simpleEntry);
In this case, the type variable T
of the Arrays.asList
method is not fixed to a particular type yet. It has an upper bound of SimpleEntry<Integer, String>
(the type of simpleEntry
).
According to the Java Language Specification, section 18.5.2, "Invocation Type Inference", the compiler will further constraint the type T
by constraining the return type of asList
(List<T>
) to the invocation context target type (List<Entry<Integer, String>>
).
This is possible; when the compiler selects T to be Entry<Integer, String>
, the whole expression fits, because a value of type SimpleEntry<Integer, String>
can be assigned to a variable of type Entry<Integer, String>
.
For list4
, the form is:
SimpleEntry<Integer, String> simpleEntry = new SimpleEntry<>(1, "1");
Optional<SimpleEntry<Integer, String>> optionalSimpleEntry = Optional.of(simpleEntry);
List<Optional<Entry<Integer, String>>> list4 = Arrays.asList<T>(optionalSimpleEntry);
Here, T
is initially constrained to an upper bound of Optional<SimpleEntry<Integer, String>>
. The target type of the expression context is List<Optional<Entry<Integer, String>>>
. It is not possible for the compiler to come up with a T
that fits both.
A value of type Optional<SimpleEntry<Integer, String>>
cannot be assigned to a variable of type Optional<Entry<Integer, String>>>
.
That's why the compiler complains.
In simpler terms, for a method where the generic type is not constrained, and there is an expression context that constrains the generic type, it works for one level deep of parameterization.
You can say
Dog dog = ...;
List<Animal> animals = Arrays.asList(dog);
But it doesn't work at a deeper level of parameterization.
Optional<Dog> optionalDog = ...;
List<Optional<Animal>> optionalAnimals = Arrays.asList(optionalDog);
Upvotes: 2
Reputation: 147164
So let's explicitly write the types we expect to infer. Also, we'll put the declaration near the use.
SimpleEntry<Integer, String> simpleEntry = ...
List<Entry<Integer, String>> list1 =
Arrays.<Entry<Integer, String>>asList(simpleEntry);
SimpleEntry<xyz>
is an Entry<xyz>
so that is fine.
Optional<Entry<Integer, String>> optionalEntry = ...
List<Optional<Entry<Integer, String>>> list2 =
Arrays.<Optional<Entry<Integer, String>>>asList(optionalEntry);
Optional<xyz>
is trivially an Optional<xyz>
.
Optional<SimpleEntry<Integer, String>> optionalSimpleEntry = ...
List<Optional<SimpleEntry<Integer, String>>> list3 =
Arrays.<Optional<SimpleEntry<Integer, String>>>asList(optionalSimpleEntry);
Optional<xyz>
is trivially an Optional<xyz>
again.
Optional<SimpleEntry<Integer, String>> optionalSimpleEntry = ...
List<Optional<Entry<Integer, String>>> list4 =
Arrays.<Optional<Entry<Integer, String>>>asList(optionalSimpleEntry);
Awooga! Optional<SimpleEntry<xyz>>
is not an Optional<Entry<xyz>>
.
You could use Optional<? extends Entry<xyz>>
Upvotes: 2