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

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

问题描述

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

  public class Foo {

public static void main(String [] args)throws Exception {
//在Java 7和Java 8中编译正常:
Class <?扩展CharSequence> aClass = true? String.class:StringBuilder.class;
CharSequence foo = foo(aClass);

//内联变量,在Java 7中编译,但不在Java 8中编译:
CharSequence foo2 = foo(true?String.class:StringBuilder.class);

}

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

汇编错误:


错误:(9,29)java:class Foo中的foo方法不能应用于给定的
类型; required:java.lang.Class found:true? Str [...] class

reason:推断的类型不符合等式约束(s)
推断:java.lang.StringBuilder
等式约束:java。 lang.StringBuilder,java.lang.String


为什么在Java 8中停止工作?这是否是故意的/一些其他功能的副作用,或者它只是一个编译器错误? 解决方案

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

在赋值或调用上下文中,引用条件表达式是多表达式。这意味着表达式的类型不是将采集转换应用于lub(T1,T2)的结果,请参阅 JSL-15.25.3 ,详细定义了T1和T2。相反,我们也从这部分规范中获得:


如果多参考条件表达式出现在特定$ b的上下文中$ b类型为目标类型T,其第二个和第三个操作数表达式在与目标类型T相同类型的上下文中类似地出现



类型多引用条件表达式与其目标类型相同。

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






好的,但为什么我们在这里得到T的平等界限呢?



让我们从调用中详细看看:

  foo true?String.class:StringBuilder.class)

其中foo是:

  static< T> T foo(Class< T> clazz)抛出Exception {
return clazz.newInstance();

$ / code>

当我们调用方法 foo( )与表达式 true? String.class:StringBuilder.class 。该引用条件表达式应该在类型为 Class< T> 的松散调用上下文中兼容。这表示为,请参阅 JLS-18.1.2

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

如下 JLS-18.2.1 我们有:


形式的约束公式被缩减如下:

...




  • 如果表达式是形式为e1的条件表达式? e2:e3,
    约束可以简化为两个约束公式:

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

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

或:

 类<字符串> →Class< T> 
Class< StringBuilder> →Class< T>

稍后从


形式的约束公式被缩减如下:

...




  • 否则,约束将减少为


我只包括相关部分。因此,我们现在有:

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

JLS-18.2.3 ,我们有:

< blockquote>

形式的约束公式被缩减如下:

...




  • 否则,约束根据T的形式被减少:


    • 如果T是参数化的类或接口类型,或
      参数化类或接口类型(直接或间接)的内部类类型,让A1,...,An为
      为T的类型参数。 S的超类型,相应的类
      或接口类型被识别,类型参数为B1,...,Bn。如果不存在这样的
      类型,则该约束将减少为假。否则,根据新的约束,约束减少到
      :对于所有i(1≤i≤n),



因此, Class< T> Class< String> Class< StringBuilder> 是参数化类,这意味着现在我们的约束减少为:

 字符串< = T 
StringBuilder< = T

同样来自 JLS-18.2.3 ,我们有:


S ,其中S和T是类型参数
(§4.5.1),如下所示:

... 如果T是一个类型:


  • 如果S是一个类型,该限制减少到




因此,我们最终得到T的这些限制:

  String = T 
StringBuilder = T

最后在 JLS-18.2.4 我们有:


形式的约束公式,其中S和T是类型,随着
的降低而减少:





$ b

  • 否则,如果T是推理变量α,那么约束将减少到绑定的
    S =α。



    并且类型变量T没有解决方案边界为 T = String T = StringBuilder 。编译器没有类型可以替代T来满足这两个限制。因此,编译器会显示错误信息。






    所以根据当前的规范,javac是可以的,正确的呢?那么应该调查7和8之间的兼容性问题。为此,我已经提交了 JDK-8044053 ,以便我们可以跟踪此问题。

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

    Compilation error:

    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

    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?

    解决方案

    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.

    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:

    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.

    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.


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

    Let's see in detail, from the call:

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

    where foo is:

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

    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>
    

    As follows from JLS-18.2.1 we have that:

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

    ...

    • If the expression is a conditional expression of the form e1 ? e2 : e3, the constraint reduces to two constraint formulas, ‹e2 → T› and ‹e3 → T›.

    This implies that we obtain the following constraint formulas:

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

    or:

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

    Later from JLS-18.2.2 we have that:

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

    ...

    • Otherwise, the constraint reduces to ‹S <: T›.

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

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

    From JLS-18.2.3, we have:

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

    ...

    • Otherwise, the constraint is reduced according to the form of T:
      • If T is a parameterized class or interface type, or an inner class type of a parameterized class or interface type (directly or indirectly), let A1, ..., An be the type arguments of T. Among the supertypes of S, a corresponding class or interface type is identified, with type arguments B1, ..., Bn. If no such type exists, the constraint reduces to false. Otherwise, the constraint reduces to the following new constraints: for all i (1 ≤ i ≤ n), ‹Bi <= Ai›.

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

    String <= T
    StringBuilder <= T
    

    Also from JLS-18.2.3, we have:

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

    ...

    • If T is a type:
      • If S is a type, the constraint reduces to ‹S = T›.

    Thus we end up with these constraints for T:

    String = T
    StringBuilder = T
    

    Finally at JLS-18.2.4 we have that:

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

    ...

    • Otherwise, if T is an inference variable, α, the constraint reduces to the bound S = α.

    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.


    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天全站免登陆