当前位置: 首页 > 面试题库 >

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

关宏毅
2023-03-14
问题内容

此类在Java 7中可以编译,但在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();
    }
}

编译错误:

错误:(9,29)java:类Foo中的方法foo无法应用于给定类型;必需:java.lang.Class找到:true吗?str […]类

原因:推断的类型不符合相等的约束推断的:java.lang.StringBuilder相等约束:java.lang.StringBuilder,java.lang.String

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


问题答案:

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

当多引用条件表达式出现在目标类型为T的特定种类的上下文中时,其第二和第三操作数表达式类似地出现在目标类型为T的相同种类的上下文中。

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

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

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

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

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

foo在哪里:

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

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

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

如下来自JLS-18.2.1的那样:

形式‹表达式→T›的约束公式可简化为:

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

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

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

要么:

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

从JLS-18.2.2以后,我们有了:

形式为‹S→T›的约束公式如下所示:

  • 否则,约束将减小为‹S <:T›。

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

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

从JLS-18.2.3开始,我们有:

形式为‹S <:T›的约束公式可简化为:

  • 否则,根据T的形式减少约束:
    * 如果T是参数化的类或接口类型,或者参数化的类或接口类型的内部类类型(直接或间接),则让A1,…,An为T的类型参数。在S的超类型中,a使用类型参数B1,…,Bn标识相应的类或接口类型。如果不存在这样的类型,约束将减少为false。否则,约束将减少为以下新约束:对于所有i(1≤i≤n),‹Bi
    <= Ai›。

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

String <= T
StringBuilder <= T

同样从JLS-18.2.3开始,我们有:

形式为‹S <= T›的约束公式,其中S和T是类型实参(第4.5.1节),可简化为:

  • 如果T是类型:
    • 如果S是类型,则约束条件减小为‹S = T›。

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

String = T
StringBuilder = T

最后在JLS-18.2.4中,我们有:

形式为‹S = T›(其中S和T为类型)的约束公式可简化为:

  • 否则,如果T是一个推断变量α,则约束条件减小到边界S =α。

并且对于类型变量T没有溶液界限T = StringT = StringBuilder。满足这两个限制的编译器没有任何类型可以替代T。因此,编译器显示错误消息。

因此,根据当前规范,javac可以,但是规范在此正确吗?好吧,应该研究7与8之间的兼容性问题。因此,我已提交了JDK-8044053,因此我们可以跟踪此问题。



 类似资料:
  • 问题内容: 我在编写一些Java代码时遇到了编译失败,我将其简化为以下测试用例: 上面的代码无法使用JDK 1.7.0_45 进行编译: 但是,使用JDK 1.8.0_05编译时不会出现任何错误。 这是Java 7实现中的错误吗?还是对Java 8中的Java语言规范进行了增强以开始允许这样做-如果是这样,有什么变化? 问题答案: JLS SE 8在(§15.2)中说: 当某些表达式出现在某些上下

  • 问题内容: 为什么会产生错误,应如何写? 错误是“找不到<接受提供的参数的重载” 问题答案: 您的三元运算符在那里不工作的 原因 是由于各种中缀运算符的优先级。您可以在此处查看运算符优先级列表。如果您看一下,会发现底部的运算符通常是放置在较大代码段之间的运算符。优先级越高,它将越紧紧抓住左侧(或右侧)的表达式。因此,您通常希望您的运算符具有非常低的关联性,因此在类似以下的表达式中: 该会抢在它两侧

  • 问题内容: 我一直不得不放置没有任何其他条件的其他条件。反正周围有吗?例如 基本上,有什么方法可以做: 现在它显示为语法错误 仅供参考,这是一些真实的示例代码: 问题答案: 首先,三元表达式不能替代if / else构造-它等同于 返回 值的if / else构造。也就是说,if / else子句是code,三元表达式是 expression ,表示它返回一个值。 这意味着几件事: 仅当在变量的左

  • 我有一个maven项目,并将intellij配置为使用Java8。我的一个同事推了一个更改,在界面中使用默认关键字。自从我同步了他的更改后,我的编译每次都失败了。 我目前被难住了,在网上找不到答案。

  • 据我所知,PowerShell似乎没有一个针对所谓三元运算符的内置表达式。 例如,在支持三元运算符的C语言中,我可以编写如下内容: 如果PowerShell中并不存在这种情况,那么实现相同结果的最佳方法(即易于阅读和维护)是什么呢?