【问题标题】:Why does Java compiler allow casting null to primitive type in ternary operator为什么Java编译器允许在三元运算符中将null转换为原始类型
【发布时间】:2018-10-01 19:19:11
【问题描述】:

这个小程序在三元运算符的行抛出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

【问题讨论】:

  • 您是否尝试过运行第一个版本的代码(尽管它可以编译)
  • 好吧,如果这是一个错误,那么它是不允许的。不过反正代码没有错。
  • @user7 是的,尝试过,它在运行时抛出了 npe,如所述。
  • 因为它是自动装箱,但实际的拆箱操作要到运行时才会发生。

标签: java compiler-errors nullpointerexception runtime-error ternary-operator


【解决方案1】:

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

在运行时,首先计算条件表达式的第一个操作数表达式。然后使用得到的布尔值来选择第二个或第三个操作数表达式。

所以在你的情况下:

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

imaging true 是一种包含复杂逻辑的方法,如果 Java 在运行时评估第一个操作数(在本例中为 true),则它是有意义的。然后,根据规则:

如果第二个和第三个操作数之一是原始类型 T,而另一个的类型是对 T 应用装箱转换(第 5.1.7 节)的结果,则条件表达式的类型是 T。

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

【讨论】:

  • 另外,并非在所有情况下都必须是兼容类型:System.out.println (yourBool? "yes" : 3) 有效且执行良好。
【解决方案2】:

编译器不允许

int i = null;

因为表达式null的类型是“空类型”,并且没有“空类型”的拆箱转换规则。 (JLS section 5.1.8)。

但是,可以这样写:

int i = (Integer) null;

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

当你写作时:

return (true ? null : 0);

在返回 int 的方法中,或者只是在您编写时:

int i = (true ? null : 0);

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

您应该注意,null 在三元表达式 (a ? b : c) 的上下文中可以“装箱”为 Integer、Float、Double 等。这在JLS 5.1.7 (boxing conversions)JLS 15.25 (Conditional operator "? :") 中有具体描述。

【讨论】:

    猜你喜欢
    • 2012-10-28
    • 1970-01-01
    • 1970-01-01
    • 2013-10-30
    • 1970-01-01
    • 1970-01-01
    • 2021-03-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多