为什么三元运算符不像有边界通配符的泛型类型?

Feu*_*mel 21 java generics ternary-operator bounded-wildcard

以下类定义了两种方法,两种方法都直观地具有相同的功能.使用两个类型列表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;
    }
}
Run Code Online (Sandbox Code Playgroud)

根据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>
Run Code Online (Sandbox Code Playgroud)

我知道查找包含三元运算符(… ? … : …)的表达式类型的规则非常复杂,但据我所知,它选择了最具体的类型,第二个和第三个参数都可以在没有显式的情况下进行转换投.在这里,这应该是,List<? super Integer> list1但事实并非如此.

我想看看为什么不是这种情况的解释,最好是参考Java语言规范,并直观地解释如果没有阻止可能出错的地方.

Sot*_*lis 13

这个答案适用于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;
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

如果您使用IDE并将鼠标悬停在上面test(list1, list2),您会注意到返回类型是

List<? extends Object>
Run Code Online (Sandbox Code Playgroud)

这是Java类型推断可以做的最好的.如果list1是a List<Object>并且list2是a List<Number>,唯一可接受的返回类型是List<? extends Object>.由于必须涵盖此案例,因此该方法必须始终返回该类型.

同样地

List<? super Integer> list = choice ? list1 : list2;
Run Code Online (Sandbox Code Playgroud)

lub(T1, T2)又是List<? extends Object>和它的捕获转换List<capture#XX of ? extends Object>.

最后,类型的引用List<capture#XX of ? extends Object>不能分配给类型的变量,List<? super Integer>因此编译器不允许它.