Home » Java » Unboxing a null boxed object throws unexpected NullPointerException

Unboxing a null boxed object throws unexpected NullPointerException

Posted by: admin December 28, 2021 Leave a comment

Questions:

If you run the following code,

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;
        }
    }
}

you will get the following stacktrace,

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

How come there’s no compiler warning or anything? IMHO it’s a pretty nasty subtlety with unboxing, or maybe I’m just naive.


Adding on to the answer provided by @Javier, if you’re using Eclipse, you need to do the following to enable this:

  1. Navigate to Window > Preferences > Java > Compiler > Errors/Warnings
  2. Expand Potential programming problems
  3. Toggle Boxing and unboxing conversions to either “Warning”, or “Error”
  4. Tap “OK”
Answers:

I don’t know what IDE are you using, but Eclipse has an option to enable warning on boxing and unboxing conversions. It is not possible to detect it as a null pointer access, since null is not immediatly unboxed, but via Bar.getId().

The expression of type Integer is unboxed into int
Foo.java line 3

###

If you try to use any method on a null or do anything that does not make sense with a null, it throws a NullPointerException.

Autounboxing is implemented with the [Integer object].intValue() method (or similar), so it throws a NullPointerException because you can’t have null invoke a method.

Hope this helps!

###

it appears that this behavior is documented in the JDK™ 5.0 Documentation,

..you can largely ignore the distinction between int and Integer, with
a few caveats. An Integer expression can have a null value. If your
program tries to autounbox null, it will throw a NullPointerException.

###

NullPointerException is a RuntimeException than IDE can not detect when compile the code.

Instead, a good practice is check null before unboxing.

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

###

Seems like a perfectly reasonable run time exception. If your main code was:

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

Nobody would be surprised about the null pointer exception. The Bar class returns a null, and a null object pointer can not be turned into a simple value. The implementation of the Bar class might be changed to initialize the id to a non-null value. This block of code can be compiled independently from the Bar class, and so assumptions about the dynamic workings of the Bar class should certainly not be coded into this block of code.

This is probably obvious, but the real solution is to use int for the id member, instead of Integer. This, then, has no issue:

private static class Bar{
    private final int id;

    public Bar(){
        this(0);
    }

    public Bar(int id){
        this.id = id;
    }

    public int getId(){
        return id;
    }
}

(But I suppose you already were aware of this 🙂 )

###

Boxing is nothing more than syntactical sugar for casting an object like Integer to the
native equivalent ‘int’. Natives can’t be null but objects can. The boxing mechanism won’t prevent NullPointerExceptions in these cases.