以下类定义了两种方法,两种方法都直观地具有相同的功能。每个函数都使用两个类型为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
则无效。它抱怨道:
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
,但事实并非如此。
我希望看到为什么不是这种情况的解释,最好是参考 Java语言规范,并直观地解释如果不是这样可能出错的地方防止。
答案 0 :(得分:13)
此答案适用于Java 7.
Java语言规范说明了conditional operator (? :
)
否则,第二个和第三个操作数是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>
。这两个都有下限。
This article详细介绍了如何计算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)
上,您会注意到返回类型为
List<? extends Object>
这是Java类型推断可以做到的最好的。如果list1
是List<Object>
且list2
是List<Number>
,则唯一可接受的返回类型是List<? extends Object>
。因为必须涵盖这种情况,所以该方法必须始终返回该类型。
同样在
中List<? super Integer> list = choice ? list1 : list2;
lub(T1, T2)
再次为List<? extends Object>
,其捕获转化为List<capture#XX of ? extends Object>
。
最后,类型List<capture#XX of ? extends Object>
的引用不能分配给List<? super Integer>
类型的变量,因此编译器不允许它。
答案 1 :(得分:1)
时间过去了,Java也在变化。我很高兴地通知您,自Java 8以来,可能由于"target typing"的引入,Feuermurmels示例编译没有问题。
relevant section of the JLS的当前版本说:
因为引用条件表达式可以是多重表达式,所以它们可以“向下传递”上下文到它们的操作数。
...
它还允许使用额外信息来改进泛型方法调用的类型检查。在Java SE 8之前,这个分配是很好的类型:
List<String> ls = Arrays.asList();
但这不是:
List<String> ls = ... ? Arrays.asList() : Arrays.asList("a","b");
上述规则允许两种作业都被认为是良好类型的。
还有一点值得注意的是,以下来自Sotirios Delimanolis的代码并未编译:
void tryIt(List<? super Integer> list1, List<? super Integer> list2) {
List<? super Integer> l1 = list1 == list2 ? list1 : list2; // Works fine
List<? super Integer> l2 = test(list1, list2); // Error: Type mismatch
}
这表明在test
的返回类型上计算类型下限时可用的信息与条件运算符的类型不同。为什么会出现这种情况我不知道,这可能是一个有趣的问题。
我使用的是jdk_1.8.0_25。