为什么在 Java 中比较 Integer 和 int 会抛出 NullPointerException? [英] Why comparing Integer with int can throw NullPointerException in Java?
问题描述
看到这种情况让我很困惑:
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
...
}
因此,因为我认为首先执行装箱操作(即 java 尝试从 null
中提取 int 值)并且比较操作的优先级较低,这就是抛出异常的原因.
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中以这种方式实现?为什么拳击比比较参考具有更高的优先级?或者为什么他们在拳击之前没有对 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 null
before boxing?
目前,当 NullPointerException
与包装的原语一起抛出而不是与 true 对象类型一起抛出时,它看起来不一致.
At the moment it looks inconsistent when NullPointerException
is thrown with wrapped primitives and is not thrown with true object types.
推荐答案
The Short Answer
重点是:
==
两个引用类型之间总是引用比较- 通常情况下,例如使用
Integer
和String
,你会想用equals
代替
==
between two reference types is always reference comparison- More often than not, e.g. with
Integer
andString
, you'd want to useequals
instead
- 引用类型将进行拆箱转换
- 拆箱
null
总是抛出NullPointerException
- The reference type will be subjected to unboxing conversion
- Unboxing
null
always throwsNullPointerException
以上语句适用于任何给定的有效 Java 代码.根据这种理解,您提供的代码段中没有任何不一致之处.
The above statements hold for any given valid Java code. With this understanding, there is no inconsistency whatsoever in the snippet you presented.
以下是相关的 JLS 部分:
Here are the relevant JLS sections:
如果相等运算符的操作数既是引用类型又是 null 类型,则该操作是对象相等.
JLS 15.21.3 Reference Equality Operators
==
and!=
If the operands of an equality operator are both of either reference type or the null type, then the operation is object equality.
这解释了以下内容:
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.这也解释了以下内容:
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:如果相等运算符的操作数两者都是数字类型,或者一个是数字类型并且另一个可以转换为数字类型,对操作数执行二进制数字提升.如果操作数的提升类型为
int
或long
,则执行整数相等测试;如果提升的类型是float 或
double`,则执行浮点相等性测试.JLS 15.21.1 Numerical Equality Operators
==
and!=
If the operands of an equality operator are both of numeric type, or one is of numeric type and the other is convertible to numeric type, binary numeric promotion is performed on the operands. If the promoted type of the operands is
int
orlong
, then an integer equality test is performed; if the promoted type isfloat or
double`, then a floating-point equality test is performed.请注意,二进制数字提升执行值集转换和拆箱转换.
Note that binary numeric promotion performs value set conversion and unboxing conversion.
这说明:
Integer i = null; if (i == 0) { //NullPointerException }
这是Effective Java 2nd Edition, Item 49: Prefer primes to boxed primes的摘录:
总而言之,只要您有选择,就优先使用基元而不是盒装基元.原始类型更简单、更快.如果您必须使用盒装原语,请小心!自动装箱减少了使用装箱原语的冗长性,但没有减少危险.当您的程序使用
==
运算符比较两个装箱原语时,它会进行身份比较,这几乎肯定不是您想要的.当您的程序执行涉及装箱和未装箱原语的混合类型计算时,它会进行拆箱,而当您的程序进行拆箱时,它可能会抛出NullPointerException
.最后,当您的程序装箱原始值时,可能会导致代价高昂且不必要的对象创建.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 throwNullPointerException
. Finally, when your program boxes primitive values, it can result in costly and unnecessary object creations.有些地方你别无选择,只能使用盒装原语,例如泛型,但除此之外,您应该认真考虑使用盒装原语的决定是否合理.
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.
- JLS 4.2.原始类型和值
- 数字类型是整数类型和浮点类型."
- JLS 4.2. Primitive Types and Values
- "The numeric types are the integral types and the floating-point types."
- 如果一个类型是数字类型,或者它是可以通过拆箱转换转换为数字类型的引用类型,则称该类型可以转换为数字类型.";
- "拆箱转换将 [...] 从
Integer
类型转换为int
类型" - "如果
r
是null
,拆箱转换会抛出一个NullPointerException
"
- "A type is said to be convertible to a numeric type if it is a numeric type, or it is a reference type that may be converted to a numeric type by unboxing conversion."
- "Unboxing conversion converts [...] from type
Integer
to typeint
" - "If
r
isnull
, unboxing conversion throws aNullPointerException
"
- When comparing two
Integers
in Java does auto-unboxing occur? - Why are these
==
but notequals()
? - Java: What’s the difference between autoboxing and casting?
- 什么是Java/C# 中 int 和 Integer 的区别?
- 是否保证 new Integer(i) ==我在 Java 中?(是的!盒子是未装箱的,而不是其他方式!)
- 为什么
int num = Integer.getInteger("123")
throwNullPointerException
? (!!!) - Java 菜鸟:仅对象上的泛型?(是的,不幸的是)
- Java
String.equals
与==
- What is the difference between an int and an Integer in Java/C#?
- Is it guaranteed that new Integer(i) == i in Java? (YES!!! The box is unboxed, not other way around!)
- Why does
int num = Integer.getInteger("123")
throwNullPointerException
? (!!!) - Java noob: generics over objects only? (yes, unfortunately)
- Java
String.equals
versus==
这篇关于为什么在 Java 中比较 Integer 和 int 会抛出 NullPointerException?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- More often than not, e.g. with
- 通常情况下,例如使用