为什么在 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
Why comparing Integer with int can throw NullPointerException in Java?
提问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 null
before boxing?
问题是:为什么它在Java中以这种方式实现?为什么拳击比比较参考具有更高的优先级?或者为什么他们没有null
在拳击之前实施验证?
At the moment it looks inconsistent when NullPointerException
is 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
Integer
andString
, you'd want to useequals
instead
- More often than not, e.g. with
==
between a reference type and a numeric primitive type is always numeric comparison- The reference type will be subjected to unboxing conversion
- Unboxing
null
always throwsNullPointerException
- While Java has many special treatments for
String
, it is in fact NOT a primitive type
==
两个引用类型之间总是引用比较- 通常情况下,例如使用
Integer
andString
,您想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
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.
JLS 15.21.1 数值相等运算符
==
和!=
如果相等运算符的操作数是两个数字类型的,或一个是数字类型的,并且另一种是可转换到数字类型,二进制数值提升时对操作数执行。如果操作数的提升类型是
int
或long
,则执行整数相等测试;如果提升的类型是float or
double`,则执行浮点相等性测试。请注意,二进制数字提升执行值集转换和拆箱转换。
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 throwNullPointerException
. 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
参考
- JLS 4.2. Primitive Types and Values
- "The numeric typesare the integral types and the floating-point types."
- JLS 5.1.8 Unboxing Conversion
- "A type is said to be convertible to a numeric typeif 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
"
- Java Language Guide/Autoboxing
- JLS 15.21.1 Numerical Equality Operators
==
and!=
- JLS 15.21.3 Reference Equality Operators
==
and!=
- JLS 5.6.2 Binary Numeric Promotion
- JLS 4.2。原始类型和值
- “数字类型是整数类型和浮点类型。”
- JLS 5.1.8 拆箱转换
- “A型被认为是可转换为数字型,如果它是数字类型,或者它是一个引用类型,其可以被转换成数字类型的取消装箱转换”。
- “拆箱转换将 [...] 从类型转换
Integer
为类型int
” - “如果
r
是null
,拆箱转换会抛出一个NullPointerException
”
- Java 语言指南/自动装箱
- JLS 15.21.1 数值相等运算符
==
和!=
- JLS 15.21.3 引用相等运算符
==
和!=
- JLS 5.6.2 二进制数字提升
Related questions
相关问题
- When comparing two
Integers
in Java does auto-unboxing occur? - Why are these
==
but notequals()
? - Java: What's the difference between autoboxing and casting?
Related questions
相关问题
- 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==
回答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 NullPointerException
is 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 i
is null
.
因此 NPE 如果i
是null
。
回答by Joachim Sauer
In i == 0
Java will try to do auto-unboxing and do a numerical comparison (i.e. "is the value stored in the wrapper object referenced by i
the same as the value 0
?").
在i == 0
Java 中将尝试进行自动拆箱并进行数值比较(即“存储在包装器对象i
中的值0
是否与值相同?”)。
Since i
is null
the unboxing will throw a NullPointerException
.
由于i
是null
拆箱将抛出一个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 i
is convertible to a numeric type and 0
is 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 I
hold a reference to an Integer
whose value is i
?"--a question which could be answered without difficulty even when I
is 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 z
with some combinations of types, it's possible to have x==y
and y==z
but x!=z
[e.g. x=16777216f y=16777216 z=16777217]. It also means that the comparison I==i
is translated as "Convert I to an int
and, if that doesn't throw an exception, compare it to i
."
Java 的开发者可以定义==
运算符直接作用于不同类型的操作数,在这种情况下,给出Integer I; int i;
比较I==i;
可能会问这样一个问题“是否I
持有对Integer
值为的引用i
?”——这个问题可以毫不费力地回答即使I
为空。不幸的是,Java 并没有直接检查不同类型的操作数是否相等;相反,它会检查语言是否允许将任一操作数的类型转换为另一个操作数的类型,如果允许,则将转换后的操作数与未转换的操作数进行比较。这种行为意味着对于变量x
,y
和z
一些类型的组合,可能有x==y
和y==z
但是x!=z
[例如 x=16777216f y=16777216 z=16777217]。这也意味着比较I==i
被翻译为“将 I 转换为 an int
,如果没有抛出异常,则将其与 进行比较i
。”