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

发布于 2024-09-12 11:22:41 字数 530 浏览 15 评论 0原文

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

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值)并且比较操作具有较低的优先级,这就是异常的原因抛出。

问题是:为什么在Java中要这样实现呢?为什么拳击比参考文献的优先级更高?或者为什么他们不在装箱前对 null 实施验证?

目前,当使用包装原语抛出 NullPointerException 时,与使用 true 对象类型抛出时,它看起来不一致。

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.

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?

At the moment it looks inconsistent when NullPointerException is thrown with wrapped primitives and is not thrown with true object types.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

晨与橙与城 2024-09-19 11:22:41

简短回答

关键点是:

  • 两个引用类型之间的 == 始终是引用比较
    • 通常情况下,例如对于IntegerString,您需要使用equals 来代替
  • 在之间使用equals而不是==引用类型和数字基元类型始终是数字比较
    • 引用类型将进行拆箱转换
    • 拆箱 null 总是抛出 NullPointerException
  • 虽然 Java 对 String 有很多特殊处理,但实际上它不是原始类型

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


长答案

以下是相关的 JLS 部分:

JLS 15.21.3 引用相等运算符 <代码>== 和 !=

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

这解释了以下内容:

Integer i = null;
String str = null;

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

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

这也解释了以下内容:

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

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

JLS 15.21.1 数值相等运算符 <代码>== 和 !=

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

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

这解释了:

Integer i = null;

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

这是Effective Java 第二版,第 49 项:更喜欢原语而不是盒装原语的摘录:

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

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

参考文献

相关问题

相关问题

The Short Answer

The key point is this:

  • == between two reference types is always reference comparison
    • More often than not, e.g. with Integer and String, you'd want to use equals instead
  • == 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 throws NullPointerException
  • While Java has many special treatments for String, it is in fact NOT a primitive type

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


The Long Answer

Here are the relevant JLS sections:

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.

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 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 or 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.

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:

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.

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

旧时光的容颜 2024-09-19 11:22:41

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

if ( i.intValue( ) == 0 )

因此,如果 i 为 NPE

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

if ( i.intValue( ) == 0 )

Hence NPE if i is null.

吻风 2024-09-19 11:22:41
if (i == 0) {  //NullPointerException
   ...
}

i 是一个 Integer,0 是一个 int,所以真正完成的事情是这样的

i.intValue() == 0

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

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.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.

巨坚强 2024-09-19 11:22:41

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

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."

北恋 2024-09-19 11:22:41

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

由于这是不可能的(如您所列,它为 null),因此会抛出 NullPointerException 。

It's because of Javas autoboxing feature. 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.

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

留蓝 2024-09-19 11:22:41

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

由于 inull,拆箱将抛出 NullPointerException

推理是这样的:

的第一句JLS § 15.21.1 数值相等运算符 == 和 != 如下所示:

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

显然,i 可转换为数字类型,而 0 是数字类型,因此对操作数执行二进制数字提升。

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

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

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

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

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?").

Since i is null the unboxing will throw a NullPointerException.

The reasoning goes like this:

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

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

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

§ 5.6.2 Binary Numeric Promotion says (among other things):

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

§ 5.1.8 Unboxing Conversion says (among other things):

If r is null, unboxing conversion throws a NullPointerException

谈下烟灰 2024-09-19 11:22:41

只需编写一个方法并调用它即可避免 NullPointerException。

public static Integer getNotNullIntValue(Integer value)
{
    if(value!=null)
    {
        return value;
    }
    return 0;
}

Simply write a method and call it to avoid NullPointerException.

public static Integer getNotNullIntValue(Integer value)
{
    if(value!=null)
    {
        return value;
    }
    return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文