小编典典

为什么三元运算符不喜欢带有有界通配符的泛型类型?

java

下列类定义了两种方法,它们在直观上都具有相同的功能。每个函数都有两个类型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_45chooseList1有效而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语言规范, 并给出直观的解释,说明如果不能避免,可能会出错。


阅读 220

收藏
2020-11-13

共1个答案

小编典典

此答案适用于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为a List<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>因此编译器不允许这样做。

2020-11-13