The following class defines two methods, both of which intuitively have the same functionality. Each function is called with two lists of type List<? super Integer>
and a boolean value which specifies which of those lists should be assigned to a local variable.
import java.util.List;
class Example {
void chooseList1(boolean choice, List<? super Integer> list1, List<? super Integer> list2) {
List<? super Integer> list;
if (choice)
list = list1;
else
list = list2;
}
void chooseList2(boolean choice, List<? super Integer> list1, List<? super Integer> list2) {
List<? super Integer> list = choice ? list1 : list2;
}
}
According to javac 1.7.0_45
, chooseList1
is valid while chooseList2
is not. It complains:
java: incompatible types
required: java.util.List<? super java.lang.Integer>
found: java.util.List<capture#1 of ? extends java.lang.Object>
I know that the rules for finding the type of an expression containing the ternary operator (… ? … : …
) are pretty complex, but as far as I understand them, it chooses the most specific type to which both the second and third arguments can be converted without an explicit cast. Here, this should be List<? super Integer> list1
but it isn't.
I'd like to see an explanation of why this isn't the case, preferably with a reference of the Java Language Specification and an intuitive explanation of what could go wrong if it wasn't prevented.
Time goes by and Java changes. I am happy to inform you that since Java 8, probably due to the introduction of "target typing", Feuermurmels example compiles without a problem.
The current version of the relevant section of the JLS says:
It's also interesting to note that the following, derived from Sotirios Delimanolis's code does not compile:
This suggests that the information available when calculating type lower bound on the return type of
test
is different from that of the type of the conditional operator. Why this is the case I have no idea, it could be an interesting question in itself.I use jdk_1.8.0_25.
This answers applies to Java 7.
The Java Language Specification states the following about the conditional operator (
? :
)In the expression
T1
isList<capture#1? super Integer>
andT2
isList<capture#2? super Integer>
. Both of these have lower bounds.This article goes into detail about how to calculate
lub(T1, T2)
(orjoin function
). Let's take an example from thereIf you use an IDE and hover over
test(list1, list2)
, you will notice the return type isThis is the best that Java's type inference can do. if
list1
was aList<Object>
andlist2
was aList<Number>
, the only acceptable return type isList<? extends Object>
. Because this case has to be covered, the method must always return that type.Similarly in
The
lub(T1, T2)
is againList<? extends Object>
and its capture conversion isList<capture#XX of ? extends Object>
.Finally, a reference of type
List<capture#XX of ? extends Object>
can not be assigned to a variable of typeList<? super Integer>
and so the compiler doesn't allow it.