将null返回为允许使用三元运算符的int,而不是if语句

Lio*_*ion 185 java autoboxing nullpointerexception conditional-operator

让我们看看以下代码段中的简单Java代码:

public class Main {

    private int temp() {
        return true ? null : 0;
        // No compiler error - the compiler allows a return value of null
        // in a method signature that returns an int.
    }

    private int same() {
        if (true) {
            return null;
            // The same is not possible with if,
            // and causes a compile-time error - incompatible types.
        } else {
            return 0;
        }
    }

    public static void main(String[] args) {
        Main m = new Main();
        System.out.println(m.temp());
        System.out.println(m.same());
    }
}
Run Code Online (Sandbox Code Playgroud)

在这个最简单的Java代码中,temp()即使函数的返回类型是int,我们尝试返回值null(通过语句return true ? null : 0;),该方法也不会发出编译器错误.编译时,这显然会导致运行时异常NullPointerException.

但是,如果我们用一个if语句(如same()方法中)表示三元运算符,它发出编译时错误,这似乎是错误的!为什么?

Ted*_*opp 115

编译器将其解释null为对a的空引用Integer,对条件运算符应用自动装箱/取消装箱规则(如Java语言规范,15.25中所述),并愉快地进行操作.这将生成一个NullPointerException运行时,您可以通过尝试来确认.

  • @Gevorg - `null`不是_boxed_到整数,它是_interpreted_作为对整数的引用(空引用,但这不是问题).没有从null构造Integer对象,因此没有理由使用NumberFormatException. (8认同)

Vla*_*lad 39

我认为,Java编译器将其解释true ? null : 0为一个Integer表达式,可以隐式转换为int可能给出的表达式NullPointerException.

对于第二种情况下,表达式null是特殊的空类型 看到,所以代码return null使得类型不匹配.

  • 我假设这与自动拳击有关?据推测,第一次返回将不会在Java 5之前编译,对吧? (2认同)

now*_*waq 32

实际上,它在Java语言规范中都有解释.

条件表达式的类型确定如下:

  • 如果第二个和第三个操作数具有相同的类型(可以是null类型),那么这就是条件表达式的类型.

因此,你的"null" (true ? null : 0)获取一个int类型,然后自动装箱到Integer.

尝试这样的方法来验证这一点(true ? null : null),你将得到编译器错误.

  • 但是规则的那个条款不适用:第二个和第三个操作数确实_not_具有相同的类型. (3认同)

Jon*_*rdy 25

if语句的情况下,null引用不被视为Integer引用,因为它没有参与强制它被解释为这样的表达式.因此,错误可以在编译时轻松捕获,因为它更明显是类型错误.

至于条件运算符,Java语言规范§15.25"条件运算符? :"在如何应用类型转换的规则中很好地回答了这个问题:

  • 如果第二个和第三个操作数具有相同的类型(可以是null类型),那么这就是条件表达式的类型.

    不适用,因为null不是int.

  • 如果第二个和第三个操作数之一是boolean类型,另一个类型是Boolean类型,那么条件表达式的类型是boolean.

    不适用,因为既没有null,也不intbooleanBoolean.

  • 如果第二个和第三个操作数之一是null类型而另一个操作数的类型是引用类型,则条件表达式的类型是该引用类型.

    不适用,因为null属于null类型,但int不是引用类型.

  • 否则,如果第二个和第三个操作数具有可转换(第5.1.8节)到数字类型的类型,则有几种情况:[...]

    适用:null被视为可转换为数字类型,并在第5.1节中定义. 8"取消装箱转换"抛出一个NullPointerException.

  • @TedHopp:Gevorg回应了我的答案的早期修订,这是不正确的.你应该忽略这种差异. (2认同)

Mar*_*ace 11

首先要记住的是Java三元运算符具有"类型",这就是编译器将确定和考虑的内容,无论第二个或第三个参数的实际/实际类型是什么.根据几个因素,三元运算符类型以不同的方式确定,如Java语言规范15.26中所示

在上面的问题中,我们应该考虑最后一个案例:

否则,第二和第三操作数分别是S1S2类型.设T1是将拳击转换应用于S1所产生的类型,让T2为应用到S2的装箱转换所产生的类型.条件表达式的类型是将捕获转换(第5.1.10节)应用于lub(T1,T2)(第15.12.2.7节)的结果.

一旦你看一下应用捕获转换(第5.1.10节)并且最重要的是在lub(T1,T2)上,这是迄今为止最复杂的情​​况.

用简单的英语和极端简化后,我们可以将过程描述为计算第二和第三参数的"最小公共超类"(是的,想想LCM).这将为我们提供三元运算符"类型".同样,我刚才所说的是极端简化(考虑实现多个通用接口的类).

例如,如果您尝试以下操作:

long millis = System.currentTimeMillis();
return(true ? new java.sql.Timestamp(millis) : new java.sql.Time(millis));
Run Code Online (Sandbox Code Playgroud)

您会注意到条件表达式的结果类型是java.util.Date因为它是Timestamp/ Timepair 的"最小公共超类" .

由于null可以自动装箱到任何东西,"最小公共超类"是Integer类,这将是上面条件表达式(三元运算符)的返回类型.返回值将是类型的空指针,Integer这将是三元运算符返回的值.

在运行时,Java虚拟机unboxes的Integer一个NullPointerException被抛出.发生这种情况是因为JVM尝试调用该函数null.intValue(),其中null是自动装箱的结果.

在我看来(因为我的观点不在Java语言规范中,很多人会发现它错了)编译器在评估你的问题中的表达式时表现不佳.鉴于您编写true ? param1 : param2的编译器应立即确定第一个参数null- 将返回,它应该生成编译器错误.这有点类似于你编写时while(true){} etc...,编译器抱怨循环下面的代码并标记它Unreachable Statements.

你的第二个案例非常简单,这个答案已经太长了......;)

更正:

经过另一次分析后,我认为我说错了null可以装箱/自动装箱任何东西.谈论类Integer,显式装箱包括调用new Integer(...)构造函数或者Integer.valueOf(int i);(我在某处找到了这个版本).前者会抛出NumberFormatException(这不会发生),而第二种情况则没有意义,因为int不能null......