Why doesn't the ternary operator like generic types with bounded wildcards?
bounded-wildcard, generics, java, ternary-operator
Solution
This answers applies to Java 7.
The Java Language Specification states the following about the conditional operator (`? :`)
Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2.
The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).
In the expression
List<? super Integer> list = choice ? list1 : list2;
`T1` is `List<capture#1? super Integer>` and `T2` is `List<capture#2? super Integer>`. Both of these have lower bounds.
This article goes into detail about how to calculate `lub(T1, T2)` (or `join function`). Let's take an example from there
<T> T pick(T a, T b) {
return null;
}
<C, A extends C, B extends C> C test(A a, B b) {
return pick(a, b); // inferred type: Object
}
void tryIt(List<? super Integer> list1, List<? super Integer> list2) {
test(list1, list2);
}
If you use an IDE and hover over `test(list1, list2)`, you will notice the return type is
List<? extends Object>
This is the best that Java's type inference can do. if `list1` was a `List<Object>` and `list2` was a `List<Number>`, the only acceptable return type is `List<? extends Object>`. Because this case has to be covered, the method must always return that type.
Similarly in
List<? super Integer> list = choice ? list1 : list2;
The `lub(T1, T2)` is again `List<? extends Object>` and its capture conversion is `List<capture#XX of ? extends Object>`.
Finally, a reference of type `List<capture#XX of ? extends Object>` can not be assigned to a variable of type `List<? super Integer>` and so the compiler doesn't allow it.
Problem
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.