2 回答
这个答案适用于 Java 7。
Java 语言规范对条件运算符 ( ? :
)进行了如下说明
否则,第二个和第三个操作数分别是 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>
。这两个都有下界。
本文详细介绍了如何计算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
是 aList<Object>
并且list2
是 a 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>
因此编译器不允许这样做。
时间流逝,Java 发生变化。我很高兴地通知您,从 Java 8 开始,可能是由于引入了“目标类型”,Feuermurmels 示例编译没有问题。
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。