取消装箱空盒装对象会引发意外的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:
- 导航到Window > Preferences > Java > Compiler > Errors / Warnings
- 展开潜在的编程问题
- 将Boxing和拆箱转换切换为“警告”或“错误”
- 点按“确定”
我不知道您使用的是什么IDE,但Eclipse可以选择在装箱和拆箱转换时启用警告。 无法将其检测为空指针访问,因为null不是立即取消装箱,而是通过Bar.getId()
。
Integer类型的表达式被取消装入int
Foo.java第3行
似乎JDK™5.0文档中记录了此行为,
..你可以在很大程度上忽略
int
和Integer
之间的区别,但有几点需要注意。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。