Skip to content
Theory · Wrappers and equality · lesson 3 of 8

Autoboxing and unboxing

In the previous lesson we put the coin into the envelope by hand, with Integer.valueOf(42), and took it out with the method intValue(). Writing that at every step is tiresome. So since Java 5 the compiler does this work: it puts the coin into the envelope and takes it out by itself. The program below has not a single valueOf and not a single intValue:

JAVA
public class Main {
    static int twice(Integer x) {
        return x * 2;
    }

    public static void main(String[] args) {
        Integer boxed = 5;
        int back = boxed;
        System.out.println(back + 1);    // 6
        System.out.println(twice(back)); // 10
    }
}

Let's go line by line:

  1. Integer boxed = 5;: on the right is an int number, on the left an Integer variable. The compiler writes the call for you: Integer boxed = Integer.valueOf(5);.
  2. int back = boxed; is the opposite case: the compiler turns the line into int back = boxed.intValue();.
  3. back + 1 is an ordinary addition of two ints, and it prints 6.
  4. twice(back) passes an int to a parameter of type Integer, and the compiler boxes the argument.
  5. Inside twice the expression x * 2 unboxes x: only numbers can be multiplied. The method returns 10.

Boxing and unboxing

Autoboxing is the automatic conversion of a primitive into a wrapper object: the compiler inserts a call to valueOf. Unboxing is the way back: the compiler inserts intValue(), and for other wrappers longValue(), doubleValue() and so on.

It happens wherever a primitive meets a wrapper:

  • in an assignment: Integer boxed = 5; and int back = boxed;;
  • in a method call: an int goes into an Integer parameter, or an Integer into an int parameter;
  • in arithmetic and in the comparisons < and >: boxed + 1 and boxed < 10 unbox the wrapper first.

Here the envelope picture is off in one important way. A person can see that they hold an envelope and not a coin. In code you cannot see the boxing: the line int back = boxed; looks like copying a number, though a method call stands behind it.

Unboxing null

That hidden method call is the trap. Only an existing object can be unboxed, and a wrapper variable can hold null:

JAVA
Integer count = null;
int total = count + 1; // ✗
TEXT
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because "<local1>" is null

The program compiles and then stops at run time. There is not a single dot in the line, but the compiler put the call count.intValue() into it. There is no envelope, so there is nowhere to take the coin from. The message says so directly:

  • Cannot invoke "java.lang.Integer.intValue()": the hidden method intValue() could not be called;
  • because "<local1>" is null: because the first local variable, count, holds null.

As in the classes theme, java Main.java does not keep the names of local variables, so the JVM refers to count by its number. Unboxing null never gives 0: it is always a NullPointerException. Interviewers love this trap: where does a NullPointerException come from in a line without a single dot?

Boxing in a loop

Autoboxing has a cost too: every new Long object takes up room on the heap. Look at this sum:

JAVA
Long total = 0L;
for (long i = 1; i <= 1_000_000; i++) {
    total += i;
}
System.out.println(total); // 500000500000

To the compiler, the line total += i reads like this: total = Long.valueOf(total.longValue() + i);. On every lap it unboxes the sum, adds and boxes the result into a new object. A million laps make about a million new objects that are no longer needed right away.

With long total = 0; the same loop creates no objects at all and runs noticeably faster. The rule is: compute with primitives, and use wrappers where you need an object or null.

Common mistake

A visit counter is declared as a field of type Integer:

JAVA
public class Main {
    static Integer visits;

    public static void main(String[] args) {
        visits++; // ✗
        System.out.println(visits);
    }
}
TEXT
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because "Main.visits" is null

Remember the default values of static fields: 0 for int, but null for any reference type. Integer is a reference type, so visits holds null, and visits++ tries to unbox it. Here the message names the field: Main.visits. A counter needs a primitive: static int visits; starts at 0.

Key points

  • Autoboxing means the compiler calls Integer.valueOf for you, and unboxing means it calls intValue(); this happens in assignments, method calls and arithmetic.
  • Unboxing null throws a NullPointerException, even in a line without a single dot.
  • A field of type Integer defaults to null, not 0.
  • Boxing in a loop creates a new object on almost every lap, so computations use primitives.