JDK 7中的泛型和三元运算符的编译错误

200*_*ess 7 java generics ternary-operator java-7 java-8

编写一些Java代码时遇到编译失败,我将其提炼为以下测试用例:

import java.util.Collections;
import java.util.List;

public class TernaryFailure {
    public static List<String> thisWorks() {
        return Collections.emptyList();
    }

    public static List<String> thisFailsToCompile() {
        return true ? Collections.emptyList() : Collections.emptyList();
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码无法javac使用JDK 1.7.0_45 进行编译:

$ javac TernaryFailure.java
TernaryFailure.java:10: error: incompatible types
        return true ? Collections.emptyList() : Collections.emptyList();
                    ^
  required: List<String>
  found:    List<Object>
1 error
Run Code Online (Sandbox Code Playgroud)

但是,它在JDK 1.8.0_05中编译时没有任何错误.

这是Java 7实现中的错误吗?或者是否对Java 8中的Java语言规范进行了增强以开始允许这样做 - 如果是这样,那么改变是什么?

Vic*_*ero 4

JLS SE 8 在 ( \xc2\xa715.2 ) 处表示:

\n\n
\n

当某些表达式出现在特定上下文中时,它们被视为多重表达式。以下形式的表达式可以是多表达式:

\n\n
    \n
  • 带括号的表达式 (\xc2\xa715.8.5)

  • \n
  • 类实例创建表达式 (\xc2\xa715.9)

  • \n
  • 方法调用表达式 (\xc2\xa715.12)

  • \n
  • 方法引用表达式 (\xc2\xa715.13)

  • \n
  • 条件表达式 (\xc2\xa715.25)

  • \n
  • Lambda 表达式 (\xc2\xa715.27)

  • \n
\n
\n\n

因此,从规范的这一部分可以清楚地看出,条件表达式(三元运算符)可以被视为聚合表达式。但并不是所有的条件表达式都可以被认为是聚合表达式,只能根据(\xc2\xa715.25)引用条件表达式。在(\xc2\xa715.25.3)中阐明了参考条件表达式可以被视为聚合表达式的条件:

\n\n
\n

如果引用条件表达式出现在赋值上下文或调用上下文 (\xc2\xa75.2.\xc2\xa75.3) 中,则该引用条件表达式是多元表达式。否则,它是一个独立的表达式。

\n\n

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

\n\n

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

\n
\n\n

检查示例中条件表达式是否出现在赋值上下文中,因为根据(\xc2\xa714.17)

\n\n
\n

当带有表达式的返回语句出现在方法声明中时,表达式必须可分配 (\xc2\xa75.2) 到该方法的声明返回类型,否则会发生编译时错误。

\n
\n\n

那么归根结底,这一切意味着什么?这意味着当条件表达式是多表达式时,目标类型将“下推”到每个操作数。这样,编译器就可以根据目标对条件的每个部分进行归因。在你的情况下,目标是List<String>. 如果我们检查emptyList()方法的定义,我们会得到:

\n\n
@SuppressWarnings("unchecked")\npublic static final <T> List<T> emptyList() {\n    return (List<T>) EMPTY_LIST;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

因此,通过 target List<String>,编译器可以推断 T == String 并且代码被接受。

\n