为什么 Java 编译器允许在三元运算符中将 null 转换为原始类型

Why does Java compiler allow casting null to primitive type in ternary operator

这个小程序在三元运算符的行抛出NullPointerException

public class Main {
    int someMethod() {
        return (true ? null : 0);
    }

    public static void main(String[] args)  {
        Main obj= new Main();
        obj.someMethod();
    }
}

我明白原因是 null 不能转换为 int

但是,问题是为什么Java编译器允许通过这种代码,而像下面这样的东西会导致编译时错误:

int i = null; //Error: incompatible types: <nulltype> cannot be converted to int

通过 Java Language Specification - Conditional Operator,Java 将在 运行-time 而非编译时评估条件表达式。这就是编译时未检测到错误的原因:

At run time, the first operand expression of the conditional expression is evaluated first. The resulting boolean value is then used to choose either the second or the third operand expression.

所以在你的情况下:

int someMethod() {
    return (true ? null : 0);
}

成像true是一个包含复杂逻辑的方法,如果Java计算第一个操作数(在本例中是true)在运行-时间。然后,根据规则:

If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.

由于第 3 个操作数 0 是原始类型 (T),因此表达式的类型将是 T 类型 (int)。因此,取消装箱对 int 的空引用将导致 NPE。

编译器不允许

int i = null;

因为表达式null的类型是"null type","null type"没有拆箱转换规则。 (JLS section 5.1.8).

但是允许这样写:

int i = (Integer) null;

因为表达式 (Integer) null 的类型为 java.lang.Integer 并且会导致拆箱,这总是导致 NullPointerException。

当你写:

return (true ? null : 0);

在 returns int 的方法中,或者只是当你写:

int i = (true ? null : 0);

编译成功。 (true ? null : 0) 的类型是 java.lang.Integer 并且变得类似于 int i = (Integer) null;

您应该注意 null 在三元表达式 (a ? b : c) 的上下文中可以 "Boxed" 作为 Integer、Float、Double 等。这在 JLS 5.1.7 (boxing conversions) and JLS 15.25 (Conditional operator "? :").

中有具体描述