Friday 6 April 2012

Pitfalls of Autoboxing and Unboxing in Java

Java 5 and above supports automatic conversion of primitive types (int, float, double etc.) to their object equivalents (Integer, Float, Double,...) in assignments and method and constructor invocations. This conversion is know as autoboxing and the reverse process is called Unboxing.For example

Integer obj = 10;  // autoboxing
int i1 = obj;          // auto-unboxing


Please visit Autoboxing and Unboxing in Java to learn more about Autoboxing and Unboxing.


Autoboxing and Unboxing has some common mistakes associated with it.

Unboxing a null value. 



Since the compiler generates a call to primitiveValue(), a NullpointerException exception occurs when unboxing an null wrapper class's reference to its primitive type.For example, the code will compile but it will throw a NullpointerException at runtime.

Long lobj = null;
long l = lobj;

The reason for this exception is, the compiler replaces the unboxing code
long l = lobj; with equivalent long l=lobj.longValue(); during compile time.So the method invocation on null, null.longValue() throws NullPointerException at runtime.


This issue can be handled by putting a null check of wrapper object before unboxing, such as:

List<Integer> list = new ArrayList<Integer>();
list.add(10);
Integer e = list.get(0);
if (e != null) {
 int i = e;
}

Primitive types can not be widened/Narrowed to the Wrapper classes and vice versa. 

 

For example, 

        Long l1 = 10;/*
                     * compilation error .Because 10 is an int type
                     * value.Widening from int to Long not possible.
                     */
        Long l2 = (long) l1;
        Long l3 = 10L;

        Byte b1 = 10;/*
                     * Compilation error.Because 10 is an int type
                     * value.Narrowing from int to Byte not possible
                     */
        Byte b2 = (byte) 10;

        Double d = 3.14;
        float f1 = d; /*
                     * compilation error.Narrowing from Double to float not
                     * possible
                     */
        Float f2 = 3.14f;
        double d1 = f2;/* No error.Widening from Float to double possible */


Notice that primitive types can't be widened/narrowed to the wrapper objects.The wrapper objects can be widened to primitive types but narrowing is not possible.

Use of == with Wrapper Objects

 

Integer i1 = 1000;
Integer i2 = 1000;
if (i1 != i2)
 System.out.println("i1 and i2: different objects");
if (i1.equals(i2))
 System.out.println("i1 and i2: meaningfully equal");

Integer i3 = 100;
Integer i4 = 100;
if (i3 == i4)
 System.out.println("13 and i4: same object");
if (i3.equals(i4))
 System.out.println("i3 and i4: meaningfully equal");


Notice that i1 and i2 are different objects (i.e. i1==i2=false) even if both of them wrap the same value 1000.But i3 and i4 are showing a different behavior, both of them representing the same object (i3==i4=true).This is because of the caching of wrapper objects.The Integer wrapper objects with values range from -128 to 127 will be cached for reuse.Here i4 is reusing the already existing Integer wrapper object i3.

So instead of using tricky == it is better to use equals() method to check whether two wrapper objects are meaningfully equal or not.


Autoboxing is not Recommended for Scientific Calculations.


 Autoboxing and auto-unboxing process is expensive.For example, the code d = a * b + c is using Integer classes for a, b, c and d, and the generated code is d.valueOf(a.intValue() * b.intValue() + c.intValue()). All these method invocations have their own overhead, so it’s usually recommended to use autoboxing when needed to store primitives in collections.

No comments:

Post a Comment