Java中的三元运算符——自动装箱

时间:2021-02-10 22:29:16

Let's look at the simple Java code in the following snippet:

让我们看看下面代码片段中的简单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());
    }
}

In this simplest of Java code, the temp() method issues no compiler error even though the return type of the function is int, and we are trying to return the value null (through the statement return true ? null : 0;). When compiled, this obviously causes the run time exception NullPointerException.

在这个最简单的Java代码中,虽然函数的返回类型是int,但是temp()方法不会发出编译错误,并且我们正在尝试返回值null(通过语句返回true ?空:0;)。当编译时,这显然会导致运行时异常NullPointerException。

However, it appears that the same thing is wrong if we represent the ternary operator with an if statement (as in the same() method), which does issue a compile-time error! Why?

但是,如果我们用if语句表示三元运算符(与在同一个()方法中一样),它会发出编译时错误!为什么?

8 个解决方案

#1


111  

The compiler interprets null as a null reference to an Integer, applies the autoboxing/unboxing rules for the conditional operator (as described in the Java Language Specification, 15.25), and moves happily on. This will generate a NullPointerException at run time, which you can confirm by trying it.

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

#2


39  

I think, the Java compiler interprets true ? null : 0 as an Integer expression, which can be implicitly converted to int, possibly giving NullPointerException.

我认为,Java编译器解释正确吗?null: 0作为一个整数表达式,可以隐式地转换为int,可能会产生NullPointerException。

For the second case, the expression null is of the special null type see, so the code return null makes type mismatch.

对于第二种情况,表达式null属于特殊的null类型,请参见,因此代码返回null会导致类型不匹配。

#3


32  

Actually, its all explained in the Java Language Specification.

实际上,这在Java语言规范中都有解释。

The type of a conditional expression is determined as follows:

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

  • If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.
  • 如果第二个和第三个操作数具有相同类型(可能是null类型),那么这就是条件表达式的类型。

Therefore the "null" in your (true ? null : 0) gets an int type and then is autoboxed to Integer.

所以你的"零"是真的吗?null: 0)获取int类型,然后将其自动装箱为整型。

Try something like this to verify this (true ? null : null) and you will get the compiler error.

试试这样来验证这个(真的吗?空:空)你会得到编译错误。

#4


24  

In the case of the if statement, the null reference is not treated as an Integer reference because it is not participating in an expression that forces it to be interpreted as such. Therefore the error can be readily caught at compile-time because it is more clearly a type error.

在if语句的情况下,null引用不被视为整数引用,因为它不参与强制将其解释为整数的表达式。因此,可以很容易地在编译时捕获错误,因为它更明显是一个类型错误。

As for the conditional operator, the Java Language Specification §15.25 “Conditional Operator ? :” answers this nicely in the rules for how type conversion is applied:

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

  • If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.

    Does not apply because null is not int.
  • 如果第二个和第三个操作数具有相同的类型(可能是null类型),那么这就是条件表达式的类型。不适用,因为null不是int。

  • If one of the second and third operands is of type boolean and the type of the other is of type Boolean, then the type of the conditional expression is boolean.

    Does not apply because neither null nor int is boolean or Boolean.
  • 如果第二个和第三个操作数中的一个是布尔型的,而另一个是布尔型的,那么条件表达式的类型就是布尔型的。不适用,因为null和int都不是布尔或布尔值。

  • If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type.

    Does not apply because null is of the null type, but int is not a reference type.
  • 如果第二个和第三个操作数中的一个是null类型,而另一个的类型是引用类型,那么条件表达式的类型就是引用类型。不适用,因为null是空类型,但是int不是引用类型。

  • Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases: […]

    Applies: null is treated as convertible to a numeric type, and is defined in §5.1.8 “Unboxing Conversion” to throw a NullPointerException.
  • 否则,如果第二个和第三个操作数类型转换(§5.1.8)数值类型,然后有几种情况:[…]应用:null作为可转换为数值类型,定义和§5.1.8“拆箱转换”抛出NullPointerException。

#5


11  

The first thing to keep in mind is that Java ternary operators have a "type", and that this is what the compiler will determine and consider no matter what the actual/real types of the second or third parameter are. Depending on several factors the ternary operator type is determined in different ways as illustrated in the Java Language Specification 15.26

首先要记住的是,Java三元运算符有一个“类型”,无论第二个或第三个参数的实际/实际类型是什么,编译器都会确定并考虑这个类型。根据几个因素,三元运算符类型以不同的方式确定,如Java语言规范15.26所示

In the question above we should consider the last case:

在上述问题中,我们应该考虑最后一种情况:

Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).

否则,第二个和第三个操作数分别是S1和S2类型。设T1是将装箱转换应用到S1的类型,设T2是将装箱转换应用到S2的类型。条件表达式的类型是应用捕获转换的结果(§5.1.10),滑(T1,T2)(§15.12.2.7)。

This is by far the most complex case once you take a look at applying capture conversion (§5.1.10) and most of all at lub(T1, T2).

这是迄今为止最复杂的情况下一旦你看看应用捕获转换(§5.1.10)最重要的是在滑(T1,T2)。

In plain English and after an extreme simplification we can describe the process as calculating the "Least Common Superclass" (yes, think of the LCM) of the second and third parameters. This will give us the ternary operator "type". Again, what I just said is an extreme simplification (consider classes that implement multiple common interfaces).

在简单的英语中,经过极度简化后,我们可以将这个过程描述为计算第二个和第三个参数的“最不常见的超类”(是的,想想LCM)。这将给我们三元运算符“type”。同样,我刚才说的是一个极端的简化(考虑实现多个公共接口的类)。

For example, if you try the following:

例如,如果你尝试以下方法:

long millis = System.currentTimeMillis();
return(true ? new java.sql.Timestamp(millis) : new java.sql.Time(millis));

You'll notice that resulting type of the conditional expression is java.util.Date since it's the "Least Common Superclass" for the Timestamp/Time pair.

您将注意到条件表达式的结果类型是java.util。日期,因为它是时间戳/时间对的“最不常见的超类”。

Since null can be autoboxed to anything, the "Least Common Superclass" is the Integer class and this will be the return type of the conditional expression (ternary operator) above. The return value will then be a null pointer of type Integer and that is what will be returned by the ternary operator.

因为null可以被自动装箱成任何类型,所以“最不常见的超类”是整数类,这将是上面条件表达式(三元运算符)的返回类型。返回值将是类型Integer的空指针,这是三元操作符返回的值。

At runtime, when the Java Virtual Machine unboxes the Integer a NullPointerException is thrown. This happens because the JVM attempts to invoke the function null.intValue(), where null is the result of autoboxing.

在运行时,当Java虚拟机打开整数时,会抛出一个NullPointerException。这是因为JVM试图调用函数null. intvalue(),其中null是自动装箱的结果。

In my opinion (and since my opinion is not in the Java Language Specification many people will find it wrong anyway) the compiler does a poor job in evaluating the expression in your question. Given that you wrote true ? param1 : param2 the compiler should determine right away that the first parameter -null- will be returned and it should generate a compiler error. This is somewhat similar to when you write while(true){} etc... and the compiler complains about the code underneath the loop and flags it with Unreachable Statements.

在我看来(由于我的观点不在Java语言规范中,许多人无论如何都会发现它是错误的),编译器在评估您问题中的表达式时做得很差。假设你写的是真的?参数1:参数2编译器应该立即确定第一个参数-null-将被返回,并且它应该生成一个编译器错误。这与您编写while(true){}等时有些相似。编译器会抱怨循环下面的代码,并用不可访问的语句标记它。

Your second case is pretty straightforward and this answer is already too long... ;)

你的第二个例子很简单,这个答案已经太长了……,)

CORRECTION:

更正:

After another analysis I believe that I was wrong to say that a null value can be boxed/autoboxed to anything. Talking about the class Integer, explicit boxing consists in invoking the new Integer(...) constructor or maybe the Integer.valueOf(int i); (I found this version somewhere). The former would throw a NumberFormatException (and this does not happen) while the second would just not make sense since an int cannot be null...

经过另一次分析,我认为我说空值可以被装箱/自动装箱的说法是错误的。在讨论类整数时,显式装箱包括调用新整数(…)构造函数,或者可能是整数。返回对象的值(int i);(我在什么地方找到了这个版本)。前者会抛出一个NumberFormatException(而这不会发生),而第二个则不会有意义,因为int不能为null……

#6


4  

Actually, in the first case the expression can be evaluated, since the compiler knows, that it must be evaluated as an Integer, however in the second case the type of the return value (null) can not be determined, so it can not be compiled. If you cast it to Integer, the code will compile.

实际上,在第一种情况下,表达式可以被求值,因为编译器知道表达式必须被求值为整数,但是在第二种情况下,返回值(null)的类型无法确定,因此无法编译。如果您将它转换为Integer,代码将被编译。

#7


2  

private int temp() {

    if (true) {
        Integer x = null;
        return x;// since that is fine because of auto-boxing 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
}

#8


0  

How about this:

这个怎么样:

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);

    }

}

The output is true, true.

输出是真实的。

Eclipse color codes the 1 in the conditional expression as autoboxed.

Eclipse颜色将条件表达式中的1编码为autoboxed。

My guess is the compiler is seeing the return type of the expression as Object.

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

#1


111  

The compiler interprets null as a null reference to an Integer, applies the autoboxing/unboxing rules for the conditional operator (as described in the Java Language Specification, 15.25), and moves happily on. This will generate a NullPointerException at run time, which you can confirm by trying it.

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

#2


39  

I think, the Java compiler interprets true ? null : 0 as an Integer expression, which can be implicitly converted to int, possibly giving NullPointerException.

我认为,Java编译器解释正确吗?null: 0作为一个整数表达式,可以隐式地转换为int,可能会产生NullPointerException。

For the second case, the expression null is of the special null type see, so the code return null makes type mismatch.

对于第二种情况,表达式null属于特殊的null类型,请参见,因此代码返回null会导致类型不匹配。

#3


32  

Actually, its all explained in the Java Language Specification.

实际上,这在Java语言规范中都有解释。

The type of a conditional expression is determined as follows:

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

  • If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.
  • 如果第二个和第三个操作数具有相同类型(可能是null类型),那么这就是条件表达式的类型。

Therefore the "null" in your (true ? null : 0) gets an int type and then is autoboxed to Integer.

所以你的"零"是真的吗?null: 0)获取int类型,然后将其自动装箱为整型。

Try something like this to verify this (true ? null : null) and you will get the compiler error.

试试这样来验证这个(真的吗?空:空)你会得到编译错误。

#4


24  

In the case of the if statement, the null reference is not treated as an Integer reference because it is not participating in an expression that forces it to be interpreted as such. Therefore the error can be readily caught at compile-time because it is more clearly a type error.

在if语句的情况下,null引用不被视为整数引用,因为它不参与强制将其解释为整数的表达式。因此,可以很容易地在编译时捕获错误,因为它更明显是一个类型错误。

As for the conditional operator, the Java Language Specification §15.25 “Conditional Operator ? :” answers this nicely in the rules for how type conversion is applied:

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

  • If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.

    Does not apply because null is not int.
  • 如果第二个和第三个操作数具有相同的类型(可能是null类型),那么这就是条件表达式的类型。不适用,因为null不是int。

  • If one of the second and third operands is of type boolean and the type of the other is of type Boolean, then the type of the conditional expression is boolean.

    Does not apply because neither null nor int is boolean or Boolean.
  • 如果第二个和第三个操作数中的一个是布尔型的,而另一个是布尔型的,那么条件表达式的类型就是布尔型的。不适用,因为null和int都不是布尔或布尔值。

  • If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type.

    Does not apply because null is of the null type, but int is not a reference type.
  • 如果第二个和第三个操作数中的一个是null类型,而另一个的类型是引用类型,那么条件表达式的类型就是引用类型。不适用,因为null是空类型,但是int不是引用类型。

  • Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases: […]

    Applies: null is treated as convertible to a numeric type, and is defined in §5.1.8 “Unboxing Conversion” to throw a NullPointerException.
  • 否则,如果第二个和第三个操作数类型转换(§5.1.8)数值类型,然后有几种情况:[…]应用:null作为可转换为数值类型,定义和§5.1.8“拆箱转换”抛出NullPointerException。

#5


11  

The first thing to keep in mind is that Java ternary operators have a "type", and that this is what the compiler will determine and consider no matter what the actual/real types of the second or third parameter are. Depending on several factors the ternary operator type is determined in different ways as illustrated in the Java Language Specification 15.26

首先要记住的是,Java三元运算符有一个“类型”,无论第二个或第三个参数的实际/实际类型是什么,编译器都会确定并考虑这个类型。根据几个因素,三元运算符类型以不同的方式确定,如Java语言规范15.26所示

In the question above we should consider the last case:

在上述问题中,我们应该考虑最后一种情况:

Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).

否则,第二个和第三个操作数分别是S1和S2类型。设T1是将装箱转换应用到S1的类型,设T2是将装箱转换应用到S2的类型。条件表达式的类型是应用捕获转换的结果(§5.1.10),滑(T1,T2)(§15.12.2.7)。

This is by far the most complex case once you take a look at applying capture conversion (§5.1.10) and most of all at lub(T1, T2).

这是迄今为止最复杂的情况下一旦你看看应用捕获转换(§5.1.10)最重要的是在滑(T1,T2)。

In plain English and after an extreme simplification we can describe the process as calculating the "Least Common Superclass" (yes, think of the LCM) of the second and third parameters. This will give us the ternary operator "type". Again, what I just said is an extreme simplification (consider classes that implement multiple common interfaces).

在简单的英语中,经过极度简化后,我们可以将这个过程描述为计算第二个和第三个参数的“最不常见的超类”(是的,想想LCM)。这将给我们三元运算符“type”。同样,我刚才说的是一个极端的简化(考虑实现多个公共接口的类)。

For example, if you try the following:

例如,如果你尝试以下方法:

long millis = System.currentTimeMillis();
return(true ? new java.sql.Timestamp(millis) : new java.sql.Time(millis));

You'll notice that resulting type of the conditional expression is java.util.Date since it's the "Least Common Superclass" for the Timestamp/Time pair.

您将注意到条件表达式的结果类型是java.util。日期,因为它是时间戳/时间对的“最不常见的超类”。

Since null can be autoboxed to anything, the "Least Common Superclass" is the Integer class and this will be the return type of the conditional expression (ternary operator) above. The return value will then be a null pointer of type Integer and that is what will be returned by the ternary operator.

因为null可以被自动装箱成任何类型,所以“最不常见的超类”是整数类,这将是上面条件表达式(三元运算符)的返回类型。返回值将是类型Integer的空指针,这是三元操作符返回的值。

At runtime, when the Java Virtual Machine unboxes the Integer a NullPointerException is thrown. This happens because the JVM attempts to invoke the function null.intValue(), where null is the result of autoboxing.

在运行时,当Java虚拟机打开整数时,会抛出一个NullPointerException。这是因为JVM试图调用函数null. intvalue(),其中null是自动装箱的结果。

In my opinion (and since my opinion is not in the Java Language Specification many people will find it wrong anyway) the compiler does a poor job in evaluating the expression in your question. Given that you wrote true ? param1 : param2 the compiler should determine right away that the first parameter -null- will be returned and it should generate a compiler error. This is somewhat similar to when you write while(true){} etc... and the compiler complains about the code underneath the loop and flags it with Unreachable Statements.

在我看来(由于我的观点不在Java语言规范中,许多人无论如何都会发现它是错误的),编译器在评估您问题中的表达式时做得很差。假设你写的是真的?参数1:参数2编译器应该立即确定第一个参数-null-将被返回,并且它应该生成一个编译器错误。这与您编写while(true){}等时有些相似。编译器会抱怨循环下面的代码,并用不可访问的语句标记它。

Your second case is pretty straightforward and this answer is already too long... ;)

你的第二个例子很简单,这个答案已经太长了……,)

CORRECTION:

更正:

After another analysis I believe that I was wrong to say that a null value can be boxed/autoboxed to anything. Talking about the class Integer, explicit boxing consists in invoking the new Integer(...) constructor or maybe the Integer.valueOf(int i); (I found this version somewhere). The former would throw a NumberFormatException (and this does not happen) while the second would just not make sense since an int cannot be null...

经过另一次分析,我认为我说空值可以被装箱/自动装箱的说法是错误的。在讨论类整数时,显式装箱包括调用新整数(…)构造函数,或者可能是整数。返回对象的值(int i);(我在什么地方找到了这个版本)。前者会抛出一个NumberFormatException(而这不会发生),而第二个则不会有意义,因为int不能为null……

#6


4  

Actually, in the first case the expression can be evaluated, since the compiler knows, that it must be evaluated as an Integer, however in the second case the type of the return value (null) can not be determined, so it can not be compiled. If you cast it to Integer, the code will compile.

实际上,在第一种情况下,表达式可以被求值,因为编译器知道表达式必须被求值为整数,但是在第二种情况下,返回值(null)的类型无法确定,因此无法编译。如果您将它转换为Integer,代码将被编译。

#7


2  

private int temp() {

    if (true) {
        Integer x = null;
        return x;// since that is fine because of auto-boxing 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
}

#8


0  

How about this:

这个怎么样:

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);

    }

}

The output is true, true.

输出是真实的。

Eclipse color codes the 1 in the conditional expression as autoboxed.

Eclipse颜色将条件表达式中的1编码为autoboxed。

My guess is the compiler is seeing the return type of the expression as Object.

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