下列类定义了两种方法,它们在直观上都具有相同的功能。每个函数都有两个类型List<? super Integer>和一个布尔值的列表来调用,该值指定应将这些列表中的哪个分配给局部变量。
List<? super Integer>
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; } }
根据javac 1.7.0_45,chooseList1有效而chooseList2无效。它抱怨:
javac 1.7.0_45
chooseList1
chooseList2
java: incompatible types required: java.util.List<? super java.lang.Integer> found: java.util.List<capture#1 of ? extends java.lang.Object>
我知道查找包含三元运算符(… ? … : …)的表达式类型的规则非常复杂,但是据我了解,它选择了最具体的类型,第二个和第三个参数都可以转换为该类型,而无需显式投。在这里,应该是,List<? super Integer> list1但不是。
… ? … : …
List<? super Integer> list1
我想解释一下为什么不是这种情况,最好参考 Java语言规范, 并给出直观的解释,说明如果不能避免,可能会出错。
此答案适用于Java 7。
Java语言规范规定了有关条件运算符()的以下内容? :
? :
否则,第二和第三操作数分别为S1和S2类型。令T1为对S1进行装箱转换所产生的类型,而T2为对S2进行装箱转换所产生的类型。 条件表达式的类型是将捕获转换(§5.1.10)应用于lub(T1,T2)(§15.12.2.7)的结果。
否则,第二和第三操作数分别为S1和S2类型。令T1为对S1进行装箱转换所产生的类型,而T2为对S2进行装箱转换所产生的类型。
条件表达式的类型是将捕获转换(§5.1.10)应用于lub(T1,T2)(§15.12.2.7)的结果。
在表达中
List<? super Integer> list = choice ? list1 : list2;
T1是List<capture#1? super Integer>并且T2是List<capture#2? super Integer>。两者都有下限。
T1
List<capture#1? super Integer>
T2
List<capture#2? super Integer>
本文详细介绍了如何计算lub(T1, T2)(或join function)。让我们从那里举个例子
lub(T1, T2)
join function
<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); }
如果使用IDE并将鼠标悬停在test(list1, list2),您会注意到返回类型为
test(list1, list2)
List<? extends Object>
这是Java的类型推断可以做的最好的事情。如果list1为a List<Object>和list2为a List<Number>,则唯一可接受的返回类型为List<? extends Object>。因为必须解决这种情况,所以该方法必须始终返回该类型。
list1
List<Object>
list2
List<Number>
同样在
该lub(T1, T2)又是List<? extends Object>和它的捕获转换List<capture#XX of ? extends Object>。
List<capture#XX of ? extends Object>
最后,List<capture#XX of ? extends Object>不能将类型的引用分配给类型的变量,List<? super Integer>因此编译器不允许这样做。