返回 null 作为 int 允许使用三元运算符,但不允许使用 if 语句
让我们看一下以下代码片段中的简单 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 代码中,即使函数的返回类型为 int,temp()
方法也不会发出编译器错误。 code>,并且我们尝试返回值 null
(通过语句 return true ? null : 0;
)。编译时,这显然会导致运行时异常NullPointerException
。
然而,如果我们用 if
语句表示三元运算符(如 same()
方法),那么似乎同样的事情是错误的,确实< /em> 发出编译时错误!为什么?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
编译器将
null
解释为对Integer
的 null 引用,对条件运算符应用自动装箱/拆箱规则(如 Java 语言规范,15.25),并且愉快地移动 在。这将在运行时生成一个 NullPointerException ,您可以通过尝试来确认。The compiler interprets
null
as a null reference to anInteger
, 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 aNullPointerException
at run time, which you can confirm by trying it.我认为,Java编译器解释
true ? null : 0
作为Integer
表达式,可以隐式转换为int
,可能会给出NullPointerException
。对于第二种情况,表达式
null
是特殊的 null 类型 see,因此代码return null
导致类型不匹配。I think, the Java compiler interprets
true ? null : 0
as anInteger
expression, which can be implicitly converted toint
, possibly givingNullPointerException
.For the second case, the expression
null
is of the special null type see, so the codereturn null
makes type mismatch.实际上,这一切都在 Java 语言规范中进行了解释。
因此,
(true ? null : 0)
中的“null”获取 int 类型,然后自动装箱为 Integer。尝试类似的方法来验证此
(true ? null : null)
,您将收到编译器错误。Actually, its all explained in the Java Language Specification.
Therefore the "null" in your
(true ? null : 0)
gets an int type and then is autoboxed to Integer.Try something like this to verify this
(true ? null : null)
and you will get the compiler error.对于
if
语句,null
引用不会被视为Integer
引用,因为它不参与表达式 这迫使它被这样解释。因此,该错误可以在编译时轻松捕获,因为它更明显是一个类型错误。至于条件运算符,Java 语言规范第 15.25 节“条件运算符
? :
”在如何应用类型转换的规则中很好地回答了这个问题:In the case of the
if
statement, thenull
reference is not treated as anInteger
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.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 三元运算符有一个“类型”,这是编译器将确定和考虑的内容,无论第二个或第三个参数的实际/真实类型是什么。根据多种因素,三元运算符类型以不同的方式确定,如 Java 语言规范 15.26
在上面的问题中,我们应该考虑最后一种情况:
一旦您查看应用捕获转换 (§5.1.10),最重要的是lub(T1, T2)。
用简单的英语并经过极端简化后,我们可以将这个过程描述为计算第二个和第三个参数的“最不常见超类”(是的,想想 LCM)。这将为我们提供三元运算符“类型”。再说一次,我刚才所说的是一种极端的简化(考虑实现多个公共接口的类)。
例如,如果您尝试以下操作:
您会注意到条件表达式的结果类型是 java.util.Date,因为它是 Timestamp 的“最不常见的超类” >/
时间
对。由于
null
可以自动装箱为任何内容,因此“最不常见的超类”是Integer
类,这将是上面条件表达式(三元运算符)的返回类型。返回值将是一个Integer
类型的空指针,这就是三元运算符返回的值。在运行时,当 Java 虚拟机拆箱
Integer
时,会抛出NullPointerException
。发生这种情况是因为 JVM 尝试调用函数null.intValue()
,其中null
是自动装箱的结果。在我看来(因为我的观点不在 Java 语言规范中,所以很多人无论如何都会发现它是错误的)编译器在评估问题中的表达式方面做得很差。鉴于您写了
true ? param1 : param2
编译器应立即确定第一个参数 -null
- 将被返回,并且应生成编译器错误。这有点类似于当您编写while(true){} 等...
时,编译器会抱怨循环下面的代码,并使用Unreachable statements
对其进行标记。你的第二种情况非常简单,这个答案已经太长了......;)
更正:
经过另一次分析,我相信我错误地认为
null
值可以被装箱/自动装箱到任何东西。谈论 Integer 类,显式装箱包括调用new Integer(...)
构造函数或者可能是Integer.valueOf(int i);
(我发现这个版本某处)。前者会抛出NumberFormatException
(这不会发生),而第二个则没有意义,因为int
不能为null
。 。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
In the question above we should consider the last case:
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).
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).
For example, if you try the following:
You'll notice that resulting type of the conditional expression is
java.util.Date
since it's the "Least Common Superclass" for theTimestamp
/Time
pair.Since
null
can be autoboxed to anything, the "Least Common Superclass" is theInteger
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 typeInteger
and that is what will be returned by the ternary operator.At runtime, when the Java Virtual Machine unboxes the
Integer
aNullPointerException
is thrown. This happens because the JVM attempts to invoke the functionnull.intValue()
, wherenull
is the result of autoboxing.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 writewhile(true){} etc...
and the compiler complains about the code underneath the loop and flags it withUnreachable Statements
.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 thenew Integer(...)
constructor or maybe theInteger.valueOf(int i);
(I found this version somewhere). The former would throw aNumberFormatException
(and this does not happen) while the second would just not make sense since anint
cannot benull
...实际上,在第一种情况下,可以对表达式进行求值,因为编译器知道它必须被计算为
Integer
,但是在第二种情况下,返回值的类型 (null< /code>) 无法确定,因此无法编译。如果将其转换为
Integer
,代码将编译。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 toInteger
, the code will compile.怎么样:
输出是 true,true。
Eclipse 将条件表达式中的 1 颜色编码为自动装箱。
我的猜测是编译器将表达式的返回类型视为 Object。
How about this:
The output is true, true.
Eclipse color codes the 1 in the conditional expression as autoboxed.
My guess is the compiler is seeing the return type of the expression as Object.