Autoboxing Sucks !

Introduction

Java 1.5 released a lot of features - notable among them were Annotation, Generics, Varargs, for-each loops and an ease-of-coding shortcut called 'Autoboxing'. Autoboxing is a way to automatically convert the primitive types to its corresponding 'Wrapper Classes'. The primary reason why we would like to convert the primitives to a wrapper class is a wrapper class' ability to hold null values. The primitives can never be null. Things were simple before Java 1.5 in the sense that when a programmer needed an object that can hold nulls as well, they would go for the wrapper classes and when they knew that it will always hold a value they would go for a primitive type declaration. One example for this is a bean class that holds a corresponding table value. Some of the values of a table row can be null. For those fields the value is declared as as a wrapper class for the database fields that will always hold a value we usually define them as primitives.

How autoboxing works

'Autoboxing' and its inverse 'Unboxing', are implemented by the compiler at compile time. The final compiled class file (bytecode) does not have any information on which objects were autoboxed.

Autoboxing (and unboxing) is a feature which can both be a boon and a bane to the programmers. This was added in Java 1.5 as a shortcut for programmers who to covert values from the primitive type to its corresponding wrapper classes. The decision to Autobox or unbox is made at the compile time. Autoboxing is, converting the primitive type to its corresponding wrapper class. Unboxing is converting the Wrapper to its corresponding primitive. This simple feature provided in Java can cause a lot of coding exceptions and performance issues, if the programmer is not careful enough. 
Have a look at the method below:

       private static List<Integer> autoBoxingList(){
              List<Integer> intList = new ArrayList<Integer>();
              for (int i=0; i<20; i++){               
                     intList.add(i);
              }
              return intList;
       }


This method just loops through the first 20 numbers (starting 0) and adds it to a java list. Remember that only objects can be added to the list and not primitives. What the compiler actually does is that it converts this code:

                intList.add(i);


to the one below

intList.add(new Integer(i));


The programmer does not have to worry about creating objects explicitly rather leave it to the compiler. Let’s see another method, this time for unboxing. The method simply takes in a list of Integers and adds them to the primitive variable called “sum”.  It basically sums up the Integer values in the List.

private static int unBoxingSum(List<Integer> iList){
              int sum = 0;        
              for (Integer i : iList){                
                     sum += i;
              }
              return sum;
       }


The code:

for (Integer i : iList){                
                     sum += i;
              }

is interpreted by the compiler as:

for (Integer i : iList){                
                     sum += i.intValue();
              }



Clearly this helps the programmer to write neat code without worrying about how the conversion or boxing is done. This can lead to many other unanticipated problems if the programmer is not careful enough, thereby affecting performance or throwing run time exceptions during execution. Let us go through the problems that you might unexpectedly face while working with autoboxing.

Needless Object Creation

Autoboxing works by transforming primitives to wrapper classes and vice-versa. That means there is a potential to create unnecessary objects. A small oversight can cost the program a lot of overhead which can affect the performance of the code. Have a look at the method below.

private long foo(){              
              Long sum = 0L;
              for (long i=0; i<1000; i++) {
                     sum += i;
              }     
              return sum;         
       }


The method simply adds up first 1000 numbers starting with 0. Of course there is more efficient way to sum a number but that is not the point here. Have a look at the declaration of the variable “sum” on the first line of the method foo().

Long sum = 0L;

Remember that Wrapper classes are “immutable” objects and any operation which you do, will result in the creation of new objects. This is how the compiler interprets it.

private void foo (){
              Long sum = new Long(0L); // note this
              for (long i = 0; i<1000; i++) {                
                     sum = new Long(sum.longValue() + i);  // and this
              }
              return sum.longValue();
                }

We are creating objects of “Long” wrapper class, which was not intended by the programmer. The simple fix would be just a line change – change “Long” to “long”. Unfortunately, the difference is too small to notice. 

private long foo(){              
              long sum = 0L; // changed from Long wrapper to long primitive
              for (long i = 0; i<1000; i++) {
                     sum += i;
              }     
              return sum;         
       }

It is suggested that you run both the codes and check the time each take to execute. The one using the “wrapper” would take very long time, comparatively. A simple rule to remember – don’t use wrapper classes for attributes when you can use the primitive types. Be extra careful when declaring the attributes with wrapper classes. It is obvious in the case of Integer or Character as their primitive type declaration are in lowercase and smaller in length – ‘int’ & ‘char’, but for ‘Long’ or ‘Boolean’, they can be easily missed.

Unboxing Exceptions

If you are thinking that issues happen only when doing autoboxing and unboxing is safe then have a look at the code below:


public class Test {
      
       private static Integer ii;
       private static int i;
       private static int j;
      
       public static void main(String... args) {             
              if (i == j){
                     System.out.println("Success ...........");
              }
             
              if (ii == j){
                     System.out.println("Success ...........");
              }     
       }     
}



What do you think the code will print? This code will compile without even a warning. It would throw a java.lang.NullPointerException at the comparison “(ii == j)”. This is a common mistake most programmers make. The mistake of not thinking about the NullPointerException, unboxing can throw. Especially when a method returns a Wrapper class, the programmer is not sure if the value returned is null or not. Always make sure that some value is assigned to the wrapper class before it is being used. Using primitives when declaring the variable, is the best bet in this case. Here too the same rule as above applies – use primitives instead of wrappers, as often as possible.

Check for equality carefully

Remember the wrappers are objects, and the comparison with “==” sign actually compares the equality of the objects rather than the equality of the values. Look at the code below and guess what values would be printed on the console.


public class WrapperEquality {
      
       private static Integer wInt1 = new Integer(100);      
       private static Integer wInt2 = new Integer(100);
       private static int pInt = 100;
             
       public static void main(String... args) {
             
              System.out.println(" ============= values =========== ");
              System.out.println("wInt1 = " + wInt1);
              System.out.println("wInt2 = " + wInt2);
              System.out.println("pInt = " + pInt);
             
              System.out.println(" ========== comparisons ========= ");
              System.out.println("wInt1 == wInt2 -> " + (wInt1 == wInt2));
              System.out.println("wInt1.equals(wInt2) -> " + (wInt1.equals(wInt2)));
              System.out.println("wInt1 == pInt -> " + (wInt1 == pInt));
              System.out.println("wInt2 == pInt -> " + (wInt2 == pInt));
       }
}


Did you try out? Check what gets printed on the console.


============= values ===========
wInt1 = 100
wInt2 = 100
pInt = 100
 ========== comparisons =========
wInt1 == wInt2 -> false
wInt1.equals(wInt2) -> true
wInt1 == pInt -> true
wInt2 == pInt -> true


I am sure you would have guessed correctly, but only because I prompted you, and you became more careful. In reality it is easy to miss the first one and assume it to be true. The first comparison, “wInt1 == wInt2 -> false”, not something we intended. Rather the comparison should have been done using the “equals” method, which compares the value. How about the primitives? When one of the elements for comparison is a primitive, compiler does an “unboxing” and then does the equality check.

The code “wInt1 == pInt” is interpreted as “wInt1.intValue() == pInt”, so these comparisons are safe (unless we have the Wrappers which are not initialized, that can give a java.lang.NullPointerException).
The lesson to take home is, if both are Wrappers, use “equals” method. If one is a primitive, then we needn’t worry. Better still - always compare using the primitive value.


     wInt1.intValue() == wInt2.intValue()


Use Primitives in method parameters and return types

In line with our policy of minimizing the use of wrapper classes, make sure that any method you write should return primitives rather than the Wrappers. Stick to this convention as far as possible, and this will reduce a lot of pain in future. Whenever an object is needed, auto-boxing would kick in and would help the programmer. It minimizes the risk of null pointers and needless object creation. Look at the code below, for performance. 


public class UsePrimitives {
             
       // bad
       private static Integer myInteger() {
              return new Integer(100);
       }
      
// good                   
private static int myInt(){
              return 100;
       }
                    
       public static void main(String... args) {
      
              // good loop
              int gSum = 0;             
              for (int i=0; i<10; i++){
                     gSum += myInt();
              }
                          
              // bad loop
              int bSum = 0;
              for (int i=0; i<10; i++){
                     bSum += myInteger();
              }
                                                      
              System.out.println("Sum=" + gSum);
              System.out.println("Sum=" + bSum);                    
       }
}


The two private methods, which are being used, can be thought of as an external API, the programmers would use. These two methods simple return the number 100, in its primitive form or as a wrapper class. Looking at the method below:


private static Integer myInteger() {
              return new Integer(100);
       }

If this method is a part of an API, the user does not know how this return value was achieved, should it be handled for null, etc. Besides, the Wrapper classes occupy more memory and are treated as Objects. Unless you need to return a collection, or there is a special need, always send the primitive type back. It is far more efficient and less error prone. You can get the Wrapper back whenever you want by assigning the primitive to the Wrapper class, anyway. In the code above, in 10 iterations the code is 15-25 times more efficient for a primitive than a wrapper. Of course the way I am using it is incorrect, and that I should have taken the object in a variable, but then, this is a demonstration of its efficiency, isn’t it?  And we need to give an API as to bad programmers as well. Should the code be equal in efficiency when I run it only once? We’ll no, it would be far less efficient even if I run it once. In my computer, the ratio of execution difference comes between 10 and 20. That is it is 10 to 20 times less efficient to use a Wrapper. 

Not only should you return the values as a primitive type, you must strive to use primitives as parameters to the methods instead of Wrappers. This has similar benefits as mentioned above. Even if you return a Wrapper, you still must strive to write methods with primitive parameters. The codes can anyway autobox the method which takes in primitive values. Check the code below. It takes variable arguments for integers and returns the sum of those integers. (The code is not doing error checking for brevity, but as an API writer you must do that)

Avoid primitive and wrapper overloading

Overloading a method with primitives and, Wrappers creates confusion for the programmer as well as the people who use those methods. If the two methods are spaced out in super and sub classes, even then take extra care. Use the annotation @Overload whenever you overload any method (more details under annotation section). Let us look at the three overloaded methods. Note this is a very bad programming practice, and just having anyone of these 3, would be sufficient for our purpose. 


       private static void foo(int int1, int int2){
              System.out.println("foo(int int1, int int2). " + (int1 + int2));
       }     
      
       private static void foo(int int1, Integer int2){
              System.out.println("foo(int int1, Integer int2). " + (int1 + int2));
       }
      
       private static void foo(Integer int1, Integer int2){
              System.out.println("foo(Integer int1, Integer int2). " + (int1 + int2));
       }


This code compiles perfectly. No issues there. Just remember, the java compiler would know that the method is overloaded, and would therefore do the exact match. The below calls would call the first, second and third methods respectively.


       foo(1, 2);
       foo(1, new Integer(2));
       foo(new Integer(1), new Integer(2));


How about the call below? Is this a legal construct, and which method would be invoked?


       foo(new Integer(1), 2);


In case of non exact match the compiler would not let you compile the code for this line because it is unable to resolve the conflict. The compiler would throw a message such as - The method foo(int, int) is ambiguous for the type <<Class Name>>.  The complier did its part by not allowing us. We should do our part by not even attempting overloading of Wrappers and primitives. If however you keep any one of the above three methods (and comment out other overloaded foo() method, the code will compile for all the four method calls, as there is no more ambiguity.


       foo(1, 2);
       foo(1, new Integer(2));
       foo(new Integer(1), new Integer(2));
       foo(new Integer(1), 2);


Conclusion

I personally prefer to use primitives as much as possible for basic operations. This saves memory and has no potential for NullPointerException. It is also more efficient in executing the code. Programmers prefer using Collections rather than arrays, and the Collection supports only object. Many times these problems manifest themselves when you work with objects of Collection. Be extra careful and look for such potential problems when working with Collections including List, Queue, etc. This rule applies to Set and Map too.
Recap.
  • ·         Never use Wrapper objects when you can do your work with primitives. Wrapper classes come with the overhead of object creation.
  • ·         Make sure you pay attention for potential null pointer exceptions.
  • ·         Be careful when doing the equality check.
  • ·         Use primitives in method parameters and return types as far as possible.
  • ·         Be careful with method overloading with primitive and wrapper parameter


Comments

Popular posts from this blog

Java Generics - 7. Upper and Lower Bounds

Java Functional Programming : 3. The java.util.function Library

Java Generics - 3. Multiple bounding