Java 8中三元运算符的泛型编译错误,但在Java 7中没有 [英] Generics compilation error with ternary operator in Java 8, but not in Java 7

查看:19
本文介绍了Java 8中三元运算符的泛型编译错误,但在Java 7中没有的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个类在 Java 7 中编译正常,但在 Java 8 中编译不正常:

This class compiles ok in Java 7, but not in Java 8:

public class Foo {

    public static void main(String[] args) throws Exception {
        //compiles fine in Java 7 and Java 8:
        Class<? extends CharSequence> aClass = true ? String.class : StringBuilder.class;
        CharSequence foo = foo(aClass);

        //Inlining the variable, compiles in Java 7, but not in Java 8:
        CharSequence foo2 = foo(true ? String.class : StringBuilder.class);

    }

    static <T> T foo(Class<T> clazz) throws Exception {
        return clazz.newInstance();
    }
}

编译错误:

Error:(9, 29) java: 类 Foo 中的方法 foo 不能应用于给定类型;要求:java.lang.Class 发现:真的?Str[...]类
原因:推断类型不符合等式约束推断:java.lang.StringBuilder等式约束:java.lang.StringBuilder,java.lang.String

Error:(9, 29) java: method foo in class Foo cannot be applied to given types; required: java.lang.Class found: true ? Str[...]class
reason: inferred type does not conform to equality constraint(s) inferred: java.lang.StringBuilder equality constraints(s): java.lang.StringBuilder,java.lang.String

为什么这在 Java 8 中停止工作了?这是故意的/其他功能的副作用,还是仅仅是编译器错误?

Why has this stopped working in Java 8? Is it intentional / a side effect of some other feature, or is it simply a compiler bug?

推荐答案

根据当前规范,这不是 javac 错误.我在这里写了一个类似的答案是这样的 问题.这里的问题或多或少是一样的.

This is not a javac bug, according to the current spec. I wrote an answer here is SO for a similar issue. Here the problem is more or less the same.

在赋值或调用上下文中,引用条件表达式是 poly 表达式.这意味着表达式的类型不是对 lub(T1, T2) 应用捕获转换的结果,参见 JSL-15.25.3 了解 T1 和 T2 的详细定义.相反,我们也从规范的这一部分获得:

On an assignment or invocation context reference conditional expressions are poly expressions. This means that the type of the expression is not the result of applying capture conversion to lub(T1, T2), see JSL-15.25.3 for a detailed definition of T1 and T2. Instead we have, also from this portion of the spec that:

poly 引用条件表达式出现在特定上下文中的地方kind 与目标类型 T,其第二个和第三个操作数表达式类似地出现在与目标类型 T 相同的上下文中.

Where a poly reference conditional expression appears in a context of a particular kind with target type T, its second and third operand expressions similarly appear in a context of the same kind with target type T.

poly 引用条件表达式的类型与其目标类型相同.

The type of a poly reference conditional expression is the same as its target type.

所以这意味着目标类型被下推到引用条件表达式的两个操作数,并且两个操作数都针对该目标类型.所以编译器最终会从两个操作数中收集约束,导致无法解决的约束集,从而导致错误.

So this means that the target type is pushed down to both operands of the reference conditional expression, and both operands are attributed against that target type. So the compiler ends up gathering constraints from both operands, leading to an unsolvable constraint set and thus an error.

好的,但是为什么我们在这里得到 T 的相等边界?

OK, but why do we get equality bounds for T here?

从通话中详细看:

foo(true ? String.class : StringBuilder.class)

foo 在哪里:

static <T> T foo(Class<T> clazz) throws Exception {
    return clazz.newInstance();
}

当我们使用表达式 true 调用方法 foo() 时,我们有这个?String.class : StringBuilder.class.该引用条件表达式应该在类型为 Class 的松散调用上下文中兼容.这表示为,参见 JLS-18.1.2:

We have that as we are invoking method foo() with the expression true ? String.class : StringBuilder.class. This reference conditional expression should be compatible in a loose invocation context with type Class<T>. This is represented as, see JLS-18.1.2:

true ? String.class : StringBuilder.class → Class<T>

如下来自JLS-18.2.1 我们有:

‹Expression → T› 形式的约束公式简化如下:

A constraint formula of the form ‹Expression → T› is reduced as follows:

...

  • 如果表达式是 e1 形式的条件表达式?e2 : e3,约束简化为两个约束公式,‹e2 → T›和‹e3 → T›.

这意味着我们得到以下约束公式:

This implies that we obtain the following constraint formulas:

String.class → Class<T>
StringBuilder.class → Class<T>

或:

Class<String> → Class<T>
Class<StringBuilder> → Class<T>

后来来自 JLS-18.2.2 我们有:

一个形如‹S→T›的约束公式简化如下:

A constraint formula of the form ‹S → T› is reduced as follows:

...

  • 否则,约束减少为‹S <: T›.

我只包括相关部分.所以我们现在有:

I'm only including the related parts. So going on we have now:

Class<String> <: Class<T>
Class<StringBuilder> <: Class<T>

来自 JLS-18.2.3,我们有:

‹S <: T› 形式的约束公式简化如下:

A constraint formula of the form ‹S <: T› is reduced as follows:

...

  • 否则,根据T的形式减少约束:
    • 如果 T 是一个参数化类或接口类型,或者一个内部类类型参数化类或接口类型(直接或间接),令 A1, ..., An 为T 的类型参数. 在 S 的超类型中,对应的类或接口类型被标识,类型参数为 B1, ..., Bn.如果没有这种类型存在,约束减少为假.否则,约束减少到遵循新的约束条件:对于所有 i (1 ≤ i ≤ n),‹Bi <= Ai›.

    因此 ClassClassClass 是参数化类,这意味着现在我们的约束减少到:

    So as Class<T>, Class<String> and Class<StringBuilder> are parameterized classes, this implies that now our constraints reduces to:

    String <= T
    StringBuilder <= T
    

    也来自 JLS-18.2.3,我们有:

    ‹S <= T› 形式的约束公式,其中 S 和 T 是类型参数(§4.5.1),减少如下:

    A constraint formula of the form ‹S <= T›, where S and T are type arguments (§4.5.1), is reduced as follows:

    ...

    • 如果 T 是一个类型:
      • 如果 S 是一个类型,则约束简化为‹S = T›.

      因此我们最终得到了 T 的这些约束:

      Thus we end up with these constraints for T:

      String = T
      StringBuilder = T
      

      最后在 JLS-18.2.4 我们有:

      ‹S = T› 形式的约束公式,其中 S 和 T 是类型,简化为如下:

      A constraint formula of the form ‹S = T›, where S and T are types, is reduced as follows:

      ...

      • 否则,如果 T 是推理变量 α,则约束减少到界限S = α.

      并且对于具有边界T = StringT = StringBuilder 的类型变量T 没有解决方案.没有任何类型编译器可以替代 T 来满足这两个限制.为此,编译器会显示错误消息.

      And there is no solution for type variable T with bounds T = String and T = StringBuilder. There is no type the compiler can substitute T for that satisfies both restrictions. For this reason the compiler displays the error message.

      所以根据当前规范,javac 是可以的,但是规范是否正确?好吧,应该调查 7 和 8 之间的兼容性问题.出于这个原因,我已提交 JDK-8044053 以便我们可以跟踪此问题.

      So javac is OK according to the current spec, but is the spec correct on this? Well there is a compatibility issue between 7 and 8 that should be investigated. For this reason I have filed JDK-8044053 so we can track this issue.

      这篇关于Java 8中三元运算符的泛型编译错误,但在Java 7中没有的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆