22
4

2 回答 2

14

这个答案适用于 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;

T1List<capture#1? super Integer>T2List<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>因此编译器不允许这样做。

于 2014-01-11T19:20:05.480 回答
1

时间流逝,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。

于 2015-04-04T15:56:21.810 回答