取消装箱空盒装对象会引发意外的NullPointerException

如果您运行以下代码,

public class Foo{ public static void main(String[] args){ int id = new Bar().getId(); // throws unexpected NullPointerException } private static class Bar{ private final Integer id; public Bar(){ this(null); } public Bar(Integer id){ this.id = id; } public Integer getId(){ return id; } } } 

你会得到以下堆栈跟踪,

 Exception in thread "main" java.lang.NullPointerException at Foo.main(Foo.java:3) 

为什么没有编译器警告或任何东西? 恕我直言,这是一个非常讨厌的微妙与拆箱,或者也许我只是天真。


添加@Javier提供的答案,如果您使用的是Eclipse,则需要执行以下操作来启用此function:

  1. 导航到Window > Preferences > Java > Compiler > Errors / Warnings
  2. 展开潜在的编程问题
  3. Boxing和拆箱转换切换为“警告”或“错误”
  4. 点按“确定”

我不知道您使用的是什么IDE,但Eclipse可以选择在装箱和拆箱转换时启用警告。 无法将其检测为空指针访问,因为null不是立即取消装箱,而是通过Bar.getId()

Integer类型的表达式被取消装入int
Foo.java第3行

似乎JDK™5.0文档中记录了此行为,

..你可以在很大程度上忽略intInteger之间的区别,但有几点需要注意。 Integer表达式可以具有空值。 如果您的程序尝试autounbox null,它将抛出NullPointerException

如果您尝试在null上使用任何方法或使用null执行任何无意义的操作,则会抛出NullPointerException

Autounboxing使用[Integer object].intValue()方法(或类似方法)实现,因此它会抛出NullPointerException因为您无法调用null方法。

希望这可以帮助!

NullPointerException是一个RuntimeException不是IDE在编译代码时无法检测到的。

相反,一个好的做法是在取消装箱之前检查null。

 int getId(){ if(id!=null){ return id; } // return other or throw a checked exception. } 

似乎是一个非常合理的运行时exception。 如果您的主要代码是:

 public static void main(String[] args){ Integer idObj = new Bar().getId(); int id = idObj; // throws NullPointerException } 

没有人会对空指针exception感到惊讶。 Bar类返回null,并且无法将null对象指针转换为简单值。 可以更改Bar类的实现以将id初始化为非null值。 这个代码块可以独立于Bar类编译,因此有关Bar类动态工作的假设当然不能编码到这个代码块中。

这可能是显而易见的,但真正的解决方案是使用int作为id成员,而不是Integer 。 那么,这没有问题:

 private static class Bar{ private final int id; public Bar(){ this(0); } public Bar(int id){ this.id = id; } public int getId(){ return id; } } 

(但我想你已经意识到了这一点:-))

拳击只不过是用于将像Integer这样的对象转换为本机等效“int”的语法糖。 原生不能为null,但对象可以。 在这些情况下,装箱机制不会阻止NullPointerExceptions。