Since version 5, Java features autoboxing, which means that int and Integer are transformed into each other as needed, without cluttering the code with explicit conversions.

What sounds cool in the beginning does have its tradeoffs once you take a closer look. For me, these tradeoffs are so massive that I do not allow autoboxing in my code at all. This article discusses the tradeoffs in detail.

Possible NullPointerExceptions

When comparing the value sets of int and Integer, I note that they are not identical: Integer has one value in addition, which is null. For boxing (i.e. transforming an int into an Integer) this can be ignored, but for unboxing, one can encounter unwanted and surprising behavior, like in the following test (which is green, btw):

@Test(expected = NullPointerException.class)
public void unboxingOfNull() {
    Integer i = null;
    int j = i;
}

Here, the assignment of null to an int causes the NullPointerException.

Comparisons using ==

When comparing individual int values, one can only use == because the equals() method is not available for primitive types. But what happens when we compare Integers this way? The following (green) test shows us what happens:

@Test
public void equalityOnInt() {
    assertTrue(7 == 7);
    assertFalse(new Integer(7) == new Integer(7));
}

If I lose track of int and Integer in my code (e.g. because it does not really matter any more anyways) I might start mixing up the comparison operators and get in trouble.

Well, you may say that one ought to use Integer.valueOf() instead of the Integer constructor. Agreed, but why is the constructor available if we should not use it? And what happens if we use valueOf? Look at the following (green) test:

@Test
public void valueOf() {
    assertTrue(Integer.valueOf(127) == Integer.valueOf(127));
    assertFalse(Integer.valueOf(128) == Integer.valueOf(128));
    assertTrue( Integer.valueOf( -128 ) == Integer.valueOf( -128 ) );
    assertFalse( Integer.valueOf( -129 ) == Integer.valueOf( -129 ) );
}

Now, this is really sick: The behaviour of the comparison depends on the value of the Integer. Even more strangely, when mixed types are used, this behaviour changes again:

@Test
public void mixedTypes() {
    assertTrue( 127 == Integer.valueOf( 127 ) );
    assertTrue( 128 == Integer.valueOf( 128 ) );
    assertTrue( 127 == new Integer( 127 ) );
    assertTrue( 128 == new Integer( 128 ) );
}

But, of course, this is an issue of Integer and is by no means related to the contents of this article, so we shall rather not dwell on this…

Obfuscating the Semantics

There may be situations where I want to use Integer because I need a semantics that states “not defined yet”. Integer provides an elegant solution for this; the ominous “-1” is no longer needed. On the other hand, there may be situations where I definitely want to have a number, no matter what. Autoboxing blurs the borders between these two, and I can no longer rely on the compiler to distinguish them and to help us clarify our code. And once these semantical issues are set straight and clearly established throughout the code, I often notice that explicit conversions are only required at very few occasions (when using frameworks, working with nullable values or implementing GUI code). In general, I prefer to use the primitives where possible and only use the reference types when I need nullables. Often I do not even need nullables, instead I use initial values that match the semantics of the numbers.

You may say: Yes of course, it all behaves as defined.

Will you guarantee to never ever forget about the caveats? - I am NOT perfect. To me this is too risky. I’d need to define too many additional test cases for all of the specialities (null, int larger than 127 or smaller than -128, etc.) to have an insurance against being human and making mistakes. Therefore, I neither need nor want autoboxing in my code.


Comments

9.11.2010 by Jens Schauder

Hmm, while your points about autoboxing (and Integers and null values and the definition of equal vs. == and primitives vs Objects) are valid, I don’t agree with the proposed solution.

Autoboxing solves a real problem: Reduction of Code Noise. It does its job. Most of its weaknesses are inherited from weaknesses of the language. Don’t blame autoboxing for them.

We might agree: If everybody stops using autoboxing as an excuse to careless (and thoughtless) mix primitives and Integers the java world would be better place.


9.11.2010 by Andreas Leidig

Good points. You are right: This post is indeed somewhat fundamentalistic. Our aim is to provoke thinking. And yes, it is a language (Java) matter. We have only seen too much code where autoboxing is used thoughtlessly. As always: If you know what you are doing and what the consequences are and you decide to do it this way, it is more than ok; as long as you start complaining about the results.