The strange world of Java autoboxing
By: Roy van Rijn, 19 June 2009So, you’ve probably been using Java 5 for a while now..? How do you like the new features?
One of the new things is autoboxing. Java is slowly realizing that having primitives wasn’t the best idea. Having only objects instead of primitives (like modern languages, for example Scala) feels more natural and fits OO-programming.
To ease the pain Java introduced this bandage: Autoboxing.
Autoboxing means automatic translation (in some cases) of primitives to the corresponding objects.
So you can do:
int primitiveFifty = 50; Integer objectFifty = primitiveFifty;
Even better, it allows you to automagically do int-calculations with objects:
Integer calculate = 10; calculate += 20;
Java translates everything into int, then back to Integer without you noticing anything. This is great! (NOT!)
Autoboxing works great with simple examples like this, but it gets spooky when you use it in combination with widening and narrowing. Let me give you an example:
Byte b = 1;
This is clear, the ‘int’ 1 will be translated without noticing to a Byte. In the following example, the output is too as expected:
Byte b = null; Byte n = 10; if(false) { n = 1; } else { n = b; } System.out.println(n);
The result of this piece of code will be ‘null’. Of course, the false will be executed, and then ‘n = b’ will be called. This causes n to be null.
Lets inline this if-then-else to do the same, but shorter:
Byte b = null; Byte n = 10; n = (false) ? 1 : b; System.out.println(n);
The code is 100% the same as the example above isn’t it, we only inlined the if…? We do if(false) and the result is either ‘n = 1′ or ‘n = b’…?
Wrong!
When you execute the code you’ll get a NullPointerException!
Why is this? Let us translate the examples back to Java 1.4 code, without using autoboxing:
Byte b = null; Byte n = Byte.valueOf(new Integer(10).byteValue()); if(false) { n = Byte.valueOf(new Integer(1).byteValue()); } else { n = b; } System.out.println(n);
As we can see, Java does a lot to go from int to Byte.
The translation of our second example is less inituative. The following ‘rules’ are from the language specification for the conditional ‘(Operand1 ? Operand2 : Operand2);’.
We have the following code in Java 5: (false) ? (int)1 : (Byte)null);
- If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.
- If one of the second and third operands is of type boolean and the type of the other is of type Boolean, then the type of the conditional expression is boolean.
- If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type.
- Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:
- If one of the operands is of type byte or Byte and the other is of type short or Short, then the type of the conditional expression is short.
- If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression of type int whose value is representable in type T, then the type of the conditional expression is T.
- If one of the operands is of type Byte and the other operand is a constant expression of type int whose value is representable in type byte, then the type of the conditional expression is byte.
- If one of the operands is of type Short and the other operand is a constant expression of type int whose value is representable in type short, then the type of the conditional expression is short.
- If one of the operands is of type; Character and the other operand is a constant expression of type int whose value is representable in type char, then the type of the conditional expression is char.
- Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands. Note that binary numeric promotion performs unboxing conversion (§5.1.8) and value set conversion (§5.1.13).
- Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).
In our case, the second (int) and third (Byte) are both convertable to numeric types. The third sub-rule applies to our case:
“If one of the operands is of type Byte and the other operand is a constant expression of type int whose value is representable in type byte, then the type of the conditional expression is byte.”
Now we can translate the inline-code to the Java 1.4 variant:
Byte b = null; Byte n = new Byte((byte)10); n = Byte.valueOf((false)? (byte)1 : b.byteValue()); System.out.println(n);
As you can see, Java tries to get the byte value in both cases. To do this on b, it needs to call .byteValue(), which results in a NullPointerException…
So how do you safely inline it? You have to make sure the two values in the conditional have the same type as the type you expect, in our case:
Byte b = null; Byte n = 10; //Make sure we have Byte = ()?Byte:Byte; n = (false)? (Byte)(byte)1 : b; System.out.println(n);
So, be alert when using autoboxing and widening/narrowing in the same piece of code. Happy coding!


21 June 2009 om 2:26 pm
There are tons of risks when you start combining primitives and wrappers. For example, check the following code:
Rewrite it to this and performance increases dramatically:
This is the output on my machine:
Total: 499999999500000000, time: 23742ms
Total: 499999999500000000, time: 687ms
Total: 499999999500000000, time: 23236ms
Total: 499999999500000000, time: 681ms
Quite a difference, not?
The following is also funny:
Luckily, we’ve got tooling to warn us about these mistakes. For example, FindBugs: http://findbugs.sourceforge.net/bugDescriptions.html#NP_BOOLEAN_RETURN_NULL
30 July 2009 om 11:05 am
It’s a big issue that primitives don’t have a null value. Thus they can’t express a “no-set” option. This is especially a problem with booleans. A value of false doens’t mean that the value has explicitly set by the user.
I try to avoid the primitive types as much as possible. Most certainly for class variables.
2 August 2009 om 7:37 pm
Peter, a boolean that can have 3 values – true, false and null – is not really a boolean, now is it?
What you should be doing instead of avoiding primitive types, is avoid the use of null completely. Write contracts. Specify invariants, preconditions and postconditions. Almost always there is no good reason to use null values. If you disallow null, code gets a lot simpler. And you safely can – and should – use primitives.
See, for example, JSR 305 (still in progress).
Additionally, design for immutability. Use final members. See Effective Java, 2nd Edition, Item 15 (Minimize mutability).
Sure, primitives in Java are open for debate. There have been lots of discussions about them. That in a true OO-language, you shouldn’t have primitives (Smalltalk, Ruby, Scala). That even without the primitives, Java could be just as fast because of the JIT compiler. And so on. But the fact is that primitives are there. Using them is okay! Especially when you don’t want null values, and do want immutability.
Vincent