为什么在 Java 中比较 Integer 和 int 会抛出 NullPointerException? [英] Why comparing Integer with int can throw NullPointerException in Java?

查看:34
本文介绍了为什么在 Java 中比较 Integer 和 int 会抛出 NullPointerException?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

看到这种情况让我很困惑:

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

重点是:

  • == 两个引用类型之间总是引用比较
    • 通常情况下,例如使用 IntegerString,你会想用 equals 代替
    • == 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
      • 引用类型将进行拆箱转换
      • 拆箱 null 总是抛出 NullPointerException
      • The reference type will be subjected to unboxing conversion
      • Unboxing null always throws NullPointerException

      以上语句适用于任何给定的有效 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:

      如果相等运算符的操作数两者都是数字类型,或者一个是数字类型并且另一个可以转换为数字类型,对操作数执行二进制数字提升.如果操作数的提升类型为intlong,则执行整数相等测试;如果提升的类型是 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 or long, then an integer equality test is performed; if the promoted type is float 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 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.

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆