java类用三木运算编译不生效,在Java 8中使用三元运算符的泛型编译错误,但不在Java 7中...

博客讨论了Java 8中一个关于条件表达式类型推断的编译错误。在Java 7中能正常编译的代码在Java 8中无法通过,原因是Java 8的编译器在处理条件表达式时,将目标类型推断到每个操作数上,导致约束不匹配的错误。博客解释了这一变化并引用了Java语言规范的相关章节,指出这不是javac的bug,而是语言规范的一个行为改变。作者提了一个JDK的Issue来跟踪这个问题。
摘要由CSDN通过智能技术生成

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 foo(Class 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 foo(Class 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. This is represented as, see JLS-18.1.2:

true ? String.class : StringBuilder.class → Class

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

StringBuilder.class → Class

or:

Class → Class

Class → Class

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 <: class>

Class <: class>

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, Class and Class 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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值