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

时间:2013-02-20 23:44:23

标签: java eclipse nullpointerexception unboxing

如果您运行以下代码,

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,则需要执行以下操作来启用此功能:

  1. 导航至 Window > 偏好设置> Java > 编译器> 错误/警告
  2. 展开潜在的编程问题
  3. 装箱和取消装箱转化切换为“警告”或“错误”
  4. 点按“确定”

6 个答案:

答案 0 :(得分:5)

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

  

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

答案 1 :(得分:5)

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

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

希望这有帮助!

答案 2 :(得分:4)

似乎JDK™ 5.0 Documentation

中记录了此行为
  

..你可以在很大程度上忽略intInteger之间的区别   一些警告。 Integer表达式可以具有空值。如果你的   程序尝试autounbox null,它将抛出NullPointerException

答案 3 :(得分:0)

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

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

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

答案 4 :(得分:0)

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

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

没有人会对空指针异常感到惊讶。 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;
    }
}

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

答案 5 :(得分:0)

拳击只不过是用于将像Integer这样的对象投射到其中的语法糖 原生等价'int'。原生不能为null,但对象可以。在这些情况下,装箱机制不会阻止NullPointerExceptions。