繁体   English   中英

作为三元运算符允许的int返回null,但if语句则不允许

[英]Returning null as an int permitted with ternary operator but not if statement

让我们在以下片段中查看简单的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());
    }
}

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

但是,如果我们用if语句(如same()方法中)表示三元运算符,那似乎是错误的,这确实会产生编译时错误! 为什么?

编译器将null解释为对Integer的null引用,为条件运算符应用自动装箱/拆箱规则(如Java语言规范15.25中所述 ),并愉快地前进。 这将在运行时生成NullPointerException ,您可以通过尝试进行确认。

我认为,Java编译器解释为true ? null : 0 true ? null : 0作为Integer表达式,可以隐式转换为int ,可能会给出NullPointerException

对于第二种情况, null表达式具有特殊的null类型, 请参见 ,因此代码return null会使类型不匹配。

实际上,所有这些都在Java Language Specification中进行了说明。

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

  • 如果第二个操作数和第三个操作数具有相同的类型(可能是null类型),则这是条件表达式的类型。

因此,您的(true ? null : 0)的“ null”将得到一个int类型,然后自动装箱为Integer。

尝试使用类似的方法进行验证(true ? null : null) ,您将得到编译器错误。

if语句的情况下, null引用不被视为Integer引用,因为它没有参与强制将其解释为这样的表达式 因此,错误很容易在编译时捕获,因为它显然是类型错误。

至于条件运算符,请参见Java语言规范§15.25“条件运算符? : ? : ”很好地回答了如何应用类型转换的规则:

  • 如果第二个操作数和第三个操作数具有相同的类型(可能是null类型),则这是条件表达式的类型。

    不适用,因为null不是int

  • 如果第二个操作数和第三个操作数之一为布尔型,而另一个的类型为布尔型,则条件表达式的类型为布尔型。

    不适用,因为nullint都不是booleanBoolean

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

    不适用,因为null是null类型,但int不是引用类型。

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

    适用:将null视为可转换为数字类型,并在§5.1.8“拆箱转换”中定义以抛出NullPointerException

首先要记住的是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));

您会注意到,条件表达式的结果类型为java.util.Date因为它是Timestamp / Time对的“最小公共超类”。

由于null可以自动装箱为任何东西,因此“ Least Common超类”是Integer类,这将是上面条件表达式(三元运算符)的返回类型。 然后,返回值将是Integer类型的空指针,即三元运算符将返回的值。

在运行时,当Java虚拟机将Integer拆箱时,将引发NullPointerException 发生这种情况是因为JVM尝试调用函数null.intValue() ,其中null是自动装箱的结果。

在我看来(由于我的意见不在Java语言规范中,所以无论如何人们都会发现它是错误的),编译器在评估问题中的表达式方面做得很差。 既然你写的是true ? param1 : param2 true ? param1 : param2编译器应该马上确定第一个参数- null -将被退回,它应该产生一个编译器错误。 这有点类似于您编写while(true){} etc...并且编译器抱怨循环下面的代码并使用Unreachable Statements对其进行标记。

您的第二种情况非常简单,这个答案已经太长了...;)

更正:

经过另一次分析,我认为我可以错将null值装箱/自动装箱到任何东西。 谈论类Integer时,显式装箱包括调用new Integer(...)构造函数,或者调用Integer.valueOf(int i); (我在某个地方找到了此版本)。 前者将抛出NumberFormatException (并且不会发生),而第二个则将变得毫无意义,因为int不能为null

实际上,由于编译器知道,在第一种情况下可以对表达式进行求值,因为编译器知道必须将其作为Integer进行求值,但是在第二种情况下,无法确定返回值的类型( null ),因此无法确定编译。 如果将其IntegerInteger ,则代码将编译。

private int temp() {

    if (true) {
        Integer x = null;
        return x;// since that is fine because of unboxing then the returned value could be null
        //in other words I can say x could be null or new Integer(intValue) or a intValue
    }

    return (true ? null : 0);  //this will be prefectly legal null would be refrence to Integer. The concept is one the returned
    //value can be Integer 
    // then null is accepted to be a variable (-refrence variable-) of Integer
}

这个怎么样:

public class ConditionalExpressionType {

    public static void main(String[] args) {

        String s = "";
        s += (true ? 1 : "") instanceof Integer;
        System.out.println(s);

        String t = "";
        t += (!true ? 1 : "") instanceof String;
        System.out.println(t);

    }

}

输出为true,true。

Eclipse将条件表达式中的1标记为自动装箱。

我的猜测是编译器将表达式的返回类型视为Object。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM