为什么在 Java 中比较 Integer 和 int 会抛出 NullPointerException?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3352791/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 22:32:39  来源:igfitidea点击:

Why comparing Integer with int can throw NullPointerException in Java?

javanullpointerexceptionboxing

提问by Roman

It was very confusing to me to observe this situation:

观察这种情况让我感到非常困惑:

Integer i = null;
String str = null;

if (i == null) {   //Nothing happens
   ...                  
}
if (str == null) { //Nothing happens

}

if (i == 0) {  //NullPointerException
   ...
}
if (str == "0") { //Nothing happens
   ...
}

So, as I think boxing operation is executed first (i.e. java tries to extract int value from null) and comparison operation has lower priority that's why the exception is thrown.

因此,因为我认为首先执行装箱操作(即 java 尝试从中提取 int 值null)并且比较操作的优先级较低,这就是抛出异常的原因。

The question is: why is it implemented in this way in Java? Why boxing has higher priority then comparing references? Or why didn't they implemented verification against nullbefore boxing?

问题是:为什么它在Java中以这种方式实现?为什么拳击比比较参考具有更高的优先级?或者为什么他们没有null在拳击之前实施验证?

At the moment it looks inconsistent when NullPointerExceptionis thrown with wrapped primitives and is not thrown with trueobject types.

目前,当NullPointerException用包装的原语抛出而不是用真正的对象类型抛出时,它看起来不一致。

采纳答案by polygenelubricants

The Short Answer

简短的回答

The key point is this:

关键是这样的:

  • ==between two reference types is always reference comparison
    • More often than not, e.g. with Integerand String, you'd want to use equalsinstead
  • ==between a reference type and a numeric primitive type is always numeric comparison
    • The reference type will be subjected to unboxing conversion
    • Unboxing nullalways throws NullPointerException
  • While Java has many special treatments for String, it is in fact NOT a primitive type
  • ==两个引用类型之间总是引用比较
    • 通常情况下,例如使用Integerand String,您想equals改用
  • ==引用类型和数字原始类型之间总是数字比较
    • 引用类型将进行拆箱转换
    • 开箱null总是抛出NullPointerException
  • 尽管 Java 对 有许多特殊处理String,但实际上它不是原始类型

The above statements hold for any given validJava code. With this understanding, there is no inconsistency whatsoever in the snippet you presented.

上述语句适用于任何给定的有效Java 代码。有了这种理解,您提供的代码段中就没有任何不一致之处。



The Long Answer

长答案

Here are the relevant JLS sections:

以下是相关的 JLS 部分:

JLS 15.21.3 Reference Equality Operators ==and !=

If the operands of an equality operator are both of either reference type or the nulltype, then the operation is object equality.

JLS 15.21.3 引用相等运算符==!=

如果相等运算符的操作数都是引用类型或类型,则该操作是对象相等。

This explains the following:

这解释了以下内容:

Integer i = null;
String str = null;

if (i == null) {   // Nothing happens
}
if (str == null) { // Nothing happens
}
if (str == "0") {  // Nothing happens
}

Both operands are reference types, and that's why the ==is reference equality comparison.

两个操作数都是引用类型,这==就是引用相等比较的原因。

This also explains the following:

这也解释了以下内容:

System.out.println(new Integer(0) == new Integer(0)); // "false"
System.out.println("X" == "x".toUpperCase()); // "false"

For ==to be numerical equality, at least one of the operand must be a numeric type:

要使==数值相等,操作数中至少有一个必须是数值类型

JLS 15.21.1 Numerical Equality Operators ==and !=

If the operands of an equality operator are bothof numeric type, or one isof numeric type and the other is convertibleto numeric type, binary numeric promotion is performed on the operands. If the promoted type of the operands is intor long, then an integer equality test is performed; if the promoted type is float ordouble`, then a floating-point equality test is performed.

Note that binary numeric promotion performs value set conversion and unboxing conversion.

JLS 15.21.1 数值相等运算符==!=

如果相等运算符的操作数是两个数字类型的,或一个是数字类型的,并且另一种是可转换到数字类型,二进制数值提升时对操作数执行。如果操作数的提升类型是intlong,则执行整数相等测试;如果提升的类型是float ordouble`,则执行浮点相等性测试。

请注意,二进制数字提升执行值集转换和拆箱转换。

This explains:

这说明:

Integer i = null;

if (i == 0) {  //NullPointerException
}

Here's an excerpt from Effective Java 2nd Edition, Item 49: Prefer primitives to boxed primitives:

这是Effective Java 2nd Edition,Item 49:Prefer primes to boxed primes的摘录:

In summary, use primitives in preference to boxed primitive whenever you have the choice. Primitive types are simpler and faster. If you must use boxed primitives, be careful! Autoboxing reduces the verbosity, but not the danger, of using boxed primitives. When your program compares two boxed primitives with the ==operator, it does an identity comparison, which is almost certainly not what you want. When your program does mixed-type computations involving boxed and unboxed primitives, it does unboxing, and when your program does unboxing, it can throw NullPointerException. Finally, when your program boxes primitive values, it can result in costly and unnecessary object creations.

总之,只要您有选择,就优先使用原语而不是盒装原语。原始类型更简单、更快。如果您必须使用盒装原语,请小心!自动装箱减少了使用装箱原语的冗长性,但没有减少危险。当您的程序将两个装箱原语与==运算符进行比较时,它会进行身份比较,这几乎肯定不是您想要的。当您的程序进行涉及装箱和未装箱原语的混合类型计算时,它会进行拆箱,而当您的程序进行拆箱时,它会抛出NullPointerException. 最后,当您的程序装箱原始值时,可能会导致成本高昂且不必要的对象创建。

There are places where you have no choice but to use boxed primitives, e.g. generics, but otherwise you should seriously consider if a decision to use boxed primitives is justified.

有些地方您别无选择,只能使用装箱原语,例如泛型,但除此之外,您应该认真考虑使用装箱原语的决定是否合理。

References

参考

Related questions

相关问题

Related questions

相关问题

回答by perdian

It's because of Javas autoboxingfeature. The compiler detects, that on the right hand side of the comparison you're using a primitive integer and needs to unbox the wrapper Integer value into a primitive int value as well.

这是因为 Java 的自动装箱功能。编译器检测到,在比较的右侧,您使用的是原始整数,并且还需要将包装器 Integer 值拆箱为原始 int 值。

Since that's not possible (it's null as you lined out) the NullPointerExceptionis thrown.

由于这是不可能的(在您划出时它为空),NullPointerException因此抛出。

回答by Alexander Pogrebnyak

Your NPE example is equivalent to this code, thanks to autoboxing:

由于自动装箱,您的 NPE 示例等效于以下代码:

if ( i.intValue( ) == 0 )

if ( i.intValue( ) == 0 )

Hence NPE if iis null.

因此 NPE 如果inull

回答by Joachim Sauer

In i == 0Java will try to do auto-unboxing and do a numerical comparison (i.e. "is the value stored in the wrapper object referenced by ithe same as the value 0?").

i == 0Java 中将尝试进行自动拆箱并进行数值比较(即“存储在包装器对象i中的值0是否与值相同?”)。

Since iis nullthe unboxing will throw a NullPointerException.

由于inull拆箱将抛出一个NullPointerException.

The reasoning goes like this:

推理是这样的:

The first sentence of JLS § 15.21.1 Numerical Equality Operators == and !=reads like this:

第一句!JLS§15.21.1数值相等运算符==和=读这样的:

If the operands of an equality operator are both of numeric type, or one is of numeric type and the other is convertible (§5.1.8) to numeric type, binary numeric promotion is performed on the operands (§5.6.2).

如果相等运算符的操作数都是数字类型,或者一个是数字类型而另一个可转换(第 5.1.8 节)为数字类型,则对操作数执行二进制数字提升(第 5.6.2 节)。

Clearly iis convertible to a numeric type and 0is a numeric type, so the binary numeric promotion is performed on the operands.

显然i可以转换为数值类型并且0是数值类型,所以对操作数进行二进制数值提升。

§ 5.6.2 Binary Numeric Promotionsays (among other things):

§ 5.6.2 二进制数字提升说(除其他外):

If any of the operands is of a reference type, unboxing conversion (§5.1.8) is performed.

如果任何操作数是引用类型,则执行拆箱转换(第 5.1.8 节)。

§ 5.1.8 Unboxing Conversionsays (among other things):

§ 5.1.8 拆箱转换说(除其他外):

If ris null, unboxing conversion throws a NullPointerException

如果r为空,拆箱转换会抛出一个NullPointerException

回答by Damian Leszczyński - Vash

if (i == 0) {  //NullPointerException
   ...
}

i is an Integer and the 0 is an int so in the what really is done is something like this

i 是一个整数,0 是一个整数,所以在真正完成的事情中是这样的

i.intValue() == 0

And this cause the nullPointer because the i is null. For String we do not have this operation, thats why is no exception there.

这会导致 nullPointer 因为 i 为空。对于 String 我们没有这个操作,这就是为什么那里也不例外。

回答by supercat

The makers of Java could have defined the ==operator to directly act upon operands of different types, in which case given Integer I; int i;the comparison I==i;could ask the question "Does Ihold a reference to an Integerwhose value is i?"--a question which could be answered without difficulty even when Iis null. Unfortunately, Java does not directly check whether operands of different types are equal; instead, it checks whether the language allows the type of either operand to be converted to the type of the other and--if it does--compares the converted operand to the non-converted one. Such behavior means that for variables x, y, and zwith some combinations of types, it's possible to have x==yand y==zbut x!=z[e.g. x=16777216f y=16777216 z=16777217]. It also means that the comparison I==iis translated as "Convert I to an intand, if that doesn't throw an exception, compare it to i."

Java 的开发者可以定义==运算符直接作用于不同类型的操作数,在这种情况下,给出Integer I; int i;比较I==i;可能会问这样一个问题“是否I持有对Integer值为的引用i?”——这个问题可以毫不费力地回答即使I为空。不幸的是,Java 并没有直接检查不同类型的操作数是否相等;相反,它会检查语言是否允许将任一操作数的类型转换为另一个操作数的类型,如果允许,则将转换后的操作数与未转换的操作数进行比较。这种行为意味着对于变量x,yz一些类型的组合,可能有x==yy==z但是x!=z[例如 x=16777216f y=16777216 z=16777217]。这也意味着比较I==i被翻译为“将 I 转换为 an int,如果没有抛出异常,则将其与 进行比较i。”