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

时间:2021-04-13 22:30:06

The following class defines two methods, both of which intuitively have the same functionality. Each function is called with two lists of type List<? super Integer> and a boolean value which specifies which of those lists should be assigned to a local variable.

以下类定义了两种方法,两种方法都直观地具有相同的功能。每个函数都使用两个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;
    }
}

According to javac 1.7.0_45, chooseList1 is valid while chooseList2 is not. It complains:

根据javac 1.7.0_45,chooseList1有效而selectList2不有效。它抱怨说:

java: incompatible types
  required: java.util.List<? super java.lang.Integer>
  found:    java.util.List<capture#1 of ? extends java.lang.Object>

I know that the rules for finding the type of an expression containing the ternary operator (… ? … : …) are pretty complex, but as far as I understand them, it chooses the most specific type to which both the second and third arguments can be converted without an explicit cast. Here, this should be List<? super Integer> list1 but it isn't.

我知道找到包含三元运算符(...?...:...)的表达式类型的规则非常复杂,但据我所知,它选择了第二个和第三个参数都可以使用的最具体的类型没有明确的演员表而被转换。这里应该是List <?超级整数> list1但不是。

I'd like to see an explanation of why this isn't the case, preferably with a reference of the Java Language Specification and an intuitive explanation of what could go wrong if it wasn't prevented.

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

2 个解决方案

#1


13  

This answers applies to Java 7.

这个答案适用于Java 7。

The Java Language Specification states the following about the conditional operator (? :)

Java语言规范说明了关于条件运算符的以下内容(?:)

Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2.

否则,第二和第三操作数分别是S1和S2类型。设T1是将拳击转换应用到S1所产生的类型,让T2为应用到S2的装箱转换所产生的类型。

The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).

条件表达式的类型是将捕获转换(第5.1.10节)应用于lub(T1,T2)(第15.12.2.7节)的结果。

In the expression

在表达中

List<? super Integer> list = choice ? list1 : list2;

T1 is List<capture#1? super Integer> and T2 is List<capture#2? super Integer>. Both of these have lower bounds.

T1是List 和T2是List 。这两者都有下限。 #2?超级整数> #1?超级整数>

This article goes into detail about how to calculate lub(T1, T2) (or join function). Let's take an example from there

本文详细介绍了如何计算lub(T1,T2)(或连接函数)。我们来自那里

<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);
}

If you use an IDE and hover over test(list1, list2), you will notice the return type is

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

List<? extends Object>

This is the best that Java's type inference can do. if list1 was a List<Object> and list2 was a List<Number>, the only acceptable return type is List<? extends Object>. Because this case has to be covered, the method must always return that type.

这是Java的类型推断可以做的最好的。如果list1是List 而list2是List ,唯一可接受的返回类型是List <? extends Object>。因为必须涵盖这种情况,所以该方法必须始终返回该类型。

Similarly in

同样在

List<? super Integer> list = choice ? list1 : list2;

The lub(T1, T2) is again List<? extends Object> and its capture conversion is List<capture#XX of ? extends Object>.

lub(T1,T2)再次列出<? extends Object>和它的捕获转换是List #xx>

Finally, a reference of type List<capture#XX of ? extends Object> can not be assigned to a variable of type List<? super Integer> and so the compiler doesn't allow it.

最后,类型为List 不能分配给List <?类型的变量。超级整数>所以编译器不允许它。 #xx的引用?>

#2


1  

Time goes by and Java changes. I am happy to inform you that since Java 8, probably due to the introduction of "target typing", Feuermurmels example compiles without a problem.

时间流逝和Java的变化。我很高兴地通知您,自Java 8以来,可能由于引入了“目标类型”,Feuermurmels示例编译没有问题。

The current version of the relevant section of the JLS says:

JLS相关部分的当前版本说:

Because reference conditional expressions can be poly expressions, they can "pass down" context to their operands.

因为引用条件表达式可以是多重表达式,所以它们可以“向下传递”上下文到它们的操作数。

...

...

It also allows use of extra information to improve type checking of generic method invocations. Prior to Java SE 8, this assignment was well-typed:

它还允许使用额外信息来改进泛型方法调用的类型检查。在Java SE 8之前,这个赋值是很好的类型:

List<String> ls = Arrays.asList();

List ls = Arrays.asList();

but this was not:

但这不是:

List<String> ls = ... ? Arrays.asList() : Arrays.asList("a","b");

List ls = ...? Arrays.asList():Arrays.asList(“a”,“b”);

The rules above allow both assignments to be considered well-typed.

上述规则允许两种分配都被认为是良好类型的。

It's also interesting to note that the following, derived from Sotirios Delimanolis's code does not compile:

值得注意的是,以下来自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
}

This suggests that the information available when calculating type lower bound on the return type of test is different from that of the type of the conditional operator. Why this is the case I have no idea, it could be an interesting question in itself.

这表明在计算返回类型的测试时类型下限时可用的信息与条件运算符的类型不同。为什么会出现这种情况我不知道,这可能是一个有趣的问题。

I use jdk_1.8.0_25.

我用的是jdk_1.8.0_25。

#1


13  

This answers applies to Java 7.

这个答案适用于Java 7。

The Java Language Specification states the following about the conditional operator (? :)

Java语言规范说明了关于条件运算符的以下内容(?:)

Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2.

否则,第二和第三操作数分别是S1和S2类型。设T1是将拳击转换应用到S1所产生的类型,让T2为应用到S2的装箱转换所产生的类型。

The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).

条件表达式的类型是将捕获转换(第5.1.10节)应用于lub(T1,T2)(第15.12.2.7节)的结果。

In the expression

在表达中

List<? super Integer> list = choice ? list1 : list2;

T1 is List<capture#1? super Integer> and T2 is List<capture#2? super Integer>. Both of these have lower bounds.

T1是List 和T2是List 。这两者都有下限。 #2?超级整数> #1?超级整数>

This article goes into detail about how to calculate lub(T1, T2) (or join function). Let's take an example from there

本文详细介绍了如何计算lub(T1,T2)(或连接函数)。我们来自那里

<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);
}

If you use an IDE and hover over test(list1, list2), you will notice the return type is

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

List<? extends Object>

This is the best that Java's type inference can do. if list1 was a List<Object> and list2 was a List<Number>, the only acceptable return type is List<? extends Object>. Because this case has to be covered, the method must always return that type.

这是Java的类型推断可以做的最好的。如果list1是List 而list2是List ,唯一可接受的返回类型是List <? extends Object>。因为必须涵盖这种情况,所以该方法必须始终返回该类型。

Similarly in

同样在

List<? super Integer> list = choice ? list1 : list2;

The lub(T1, T2) is again List<? extends Object> and its capture conversion is List<capture#XX of ? extends Object>.

lub(T1,T2)再次列出<? extends Object>和它的捕获转换是List #xx>

Finally, a reference of type List<capture#XX of ? extends Object> can not be assigned to a variable of type List<? super Integer> and so the compiler doesn't allow it.

最后,类型为List 不能分配给List <?类型的变量。超级整数>所以编译器不允许它。 #xx的引用?>

#2


1  

Time goes by and Java changes. I am happy to inform you that since Java 8, probably due to the introduction of "target typing", Feuermurmels example compiles without a problem.

时间流逝和Java的变化。我很高兴地通知您,自Java 8以来,可能由于引入了“目标类型”,Feuermurmels示例编译没有问题。

The current version of the relevant section of the JLS says:

JLS相关部分的当前版本说:

Because reference conditional expressions can be poly expressions, they can "pass down" context to their operands.

因为引用条件表达式可以是多重表达式,所以它们可以“向下传递”上下文到它们的操作数。

...

...

It also allows use of extra information to improve type checking of generic method invocations. Prior to Java SE 8, this assignment was well-typed:

它还允许使用额外信息来改进泛型方法调用的类型检查。在Java SE 8之前,这个赋值是很好的类型:

List<String> ls = Arrays.asList();

List ls = Arrays.asList();

but this was not:

但这不是:

List<String> ls = ... ? Arrays.asList() : Arrays.asList("a","b");

List ls = ...? Arrays.asList():Arrays.asList(“a”,“b”);

The rules above allow both assignments to be considered well-typed.

上述规则允许两种分配都被认为是良好类型的。

It's also interesting to note that the following, derived from Sotirios Delimanolis's code does not compile:

值得注意的是,以下来自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
}

This suggests that the information available when calculating type lower bound on the return type of test is different from that of the type of the conditional operator. Why this is the case I have no idea, it could be an interesting question in itself.

这表明在计算返回类型的测试时类型下限时可用的信息与条件运算符的类型不同。为什么会出现这种情况我不知道,这可能是一个有趣的问题。

I use jdk_1.8.0_25.

我用的是jdk_1.8.0_25。