Java 8中三元运算符的泛型编译错误,但Java 7中没有

Java 8中三元运算符的泛型编译错误,但Java 7中没有,java,generics,java-8,Java,Generics,Java 8,此类在Java 7中编译ok,但在Java 8中编译ok: 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.cla

此类在Java 7中编译ok,但在Java 8中编译ok:

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();
    }
}
公共类Foo{
公共静态void main(字符串[]args)引发异常{
//在Java 7和Java 8中编译良好:

Class我要冒一个险说,这个错误(尽管它可能或可能不符合更新的JLS,我承认我没有详细阅读),是由于JDK 8编译器在类型处理上的不一致造成的

一般来说,三元运算符使用相同的类型推断,就像两参数方法一样,其形式参数均基于相同的类型参数。例如:

static <T> T foo(Class<? extends T> clazz, Class<? extends T> clazz2) { return null; }

public static void main(String[] args) {
    CharSequence foo2 = foo(String.class, StringBuilder.class);
}

这几乎与上述完全相同的类型推断,但在这种情况下,考虑三元算子是这样的一种方法:

static <T> T ternary(boolean cond, T a, T b) {
    if (cond) return a;
    else return b;
}
静态T三元(布尔cond,ta,tb){
如果(cond)返回a;
否则返回b;
}

因此,在这种情况下,如果将String.class和StringBuilder.class作为参数传递,则推断出的T类型(粗略地说)是
class问题仅出现在参数和赋值的上下文中

CharSequence cs1=(true? String.class: StringBuilder.class).newInstance();
有效。与其他答案声明不同,使用泛型
T三元(boolean cond,ta,tb)
方法不起作用。当调用传递给像
T foo(Class clazz)
这样的泛型方法,以便搜索
的实际类型时,这仍然失败。但是在赋值示例中它起作用

Class<? extends CharSequence> aClass = true ? String.class : StringBuilder.class;

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

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

其中多边形引用条件表达式出现在特定 对于目标类型T,其第二个和第三个操作数表达式类似 在目标类型为T的同类上下文中

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

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


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

让我们从电话中详细了解一下:

foo(true ? String.class : StringBuilder.class)
其中foo是:

static <T> T foo(Class<T> clazz) throws Exception {
    return clazz.newInstance();
}
我们从中得到如下信息:

形式è表达式的约束公式→ T›减少如下:

  • 如果表达式是形式为e1?e2:e3的条件表达式,则 约束减少为两个约束公式,èe2→ T›和èe3→ T›
这意味着我们得到以下约束公式:

String.class → Class<T>
StringBuilder.class → Class<T>
最后,我们有:

形式为èS=T›的约束公式,其中S和T为类型,减少为 如下:

  • 否则,如果T是一个推理变量α,则约束将减少到界 S=α
对于类型变量T,没有边界为
T=String
T=StringBuilder
的解决方案。编译器无法替换满足这两个限制的类型。因此,编译器会显示错误消息



因此,根据当前规范,javac是可以的,但是规范在这方面是否正确?7和8之间存在一个兼容性问题,应该进行调查。因此,我提交了文件,以便我们可以跟踪这个问题。

这是因为它改进了类型推断。@berry120您认为这是一个改进吗?:)在这个示例中,我看到了point你想说得更清楚些-我同意,在我看来,三元运算符并没有以它应该做的方式推断类型。@AleksanderBlomskøld在答案方面有了一些进展。你可能想回顾一下。你在使用什么IDE?这在使用Java 8的新Eclipse上运行得很好。我正在编写相同的答案。我想当然,我同意你的看法。嗯,我只是用Netbeans 7.4和RC的
javac
进行了尝试,它为
三元(…,…,…)生成了非常相同的错误消息
方法至于三元运算符
…?…
@Holger,你是对的,我的错误。我已经相应地编辑了答案。@davmac我的答案不正确,请检查我的答案thread@vrz请参阅我对您答案的评论。“问题是算法被指定为查找“最小上界”然后应用“捕获转换”。对于两种类型的类和类,第一步已经失败-但为什么失败?最小上限应为
Class@davmac:我没有说它是正确的,我只是把它固定在这一点上,但像你一样,我无法在新规范中确定“身份约束”的相关部分(目前)。也许我有更多的时间就可以完成了,但我想告诉大家目前为止我发现了什么。但在我看来,这么难读的规范一定会在实现中产生问题…@Holger,观察得很好。如果引用条件表达式没有出现在赋值或调用上下文中,那么它被认为是一个独立的expression。由于它不是一个多边形表达式,因此javac 8机制不适用于它,并且它的类型与javac 7获得的类型相同。这可能值得仔细检查。您的建议是,JLS在Java 7中实现的方式与Java 8中的实现方式有误。我没有时间解释呃,在
foo(true ? String.class : StringBuilder.class)
static <T> T foo(Class<T> clazz) throws Exception {
    return clazz.newInstance();
}
true ? String.class : StringBuilder.class → Class<T>
String.class → Class<T>
StringBuilder.class → Class<T>
Class<String> → Class<T>
Class<StringBuilder> → Class<T>
Class<String> <: Class<T>
Class<StringBuilder> <: Class<T>
String <= T
StringBuilder <= T
String = T
StringBuilder = T