Safe Unboxing in Java

In Java, there are two categories of data types: Primitives and Wrapper Classes. Wrapper Classes are some special types of Objects, in other words, Referenced Types. Each primitive has its corresponding wrapper class, for example, int for Integer, double for Double, and so on. You can easily convert each primitive variable into its corresponding wrapper class variable. This feature is called Boxing. In the opposite direction, wrapper class variables also can be converted into primitive variables. It is called Unboxing, and their short example is displayed below:

float f1 = 3.14f;         // Primitive Variable
Float f2 = new Float(f1); // Boxing

System.out.println(f2);   // print '3.14'

Double d1 = new Double(4.2);  // Wrapper Class Variable
double d2 = d1.doubleValue(); // Unboxing
System.out.println(d2);       // print '4.2'

As primitives cannot be stored in Collections(List, Set, …), this feature is used very frequently. But there is a crack. All variables of object types including wrapper classes can be null. In contrast, primitive variables cannot be null.

So, If you try to unbox null value, NullPointerException is occurred. Because of that, Safe Unboxing has nothing to do but being cumbersome:

Double d1 = new Double(4.2);
double d2 = 0.0;

// try - catch: it is cumbersome!!
try {
    d2 = d1.doubleValue();
} catch (NullPointerException e) {
    e.printStackTrace();
}

System.out.println(d2);

The Autoboxing / Unboxing feature that is introduced from Java 5 made the situation worse. As This feature performs boxing & unboxing automatically, NullPointerException is not handled properly. It is why this feature is controvertible.

Double d1 = null;
double d2 = d1; // auto unboxing - NullPointerException will be Occurred.

One easy solution for this problem is this: If you can be convinced that the variable you are trying to unbox cannot be null, use auto unboxing. In other situations, use a utility method to handle this problem. What I use is like following:

public class Numbers {
    private Numbers() { }

    public static boolean valueOf(Boolean b) {
        return valueOf(b, false);
    }

    public static boolean valueOf(Boolean b1, boolean b2) {
        if (null == b1) {
            return b2;
        } else {
            return b1.booleanValue();
        }
    }

    public static byte valueOf(Byte b) {
        return valueOf(b, (byte)0);
    }

    public static byte valueOf(Byte b1, byte b2) {
        if (null == b1) {
            return b2;
        } else {
            return b1.byteValue();
        }
    }

    public static char valueOf(Character c) {
        return valueOf(c, 'u0000');
    }

    public static char valueOf(Character c1, char c2) {
        if (null == c1) {
            return c2;
        } else {
            return c1.charValue();
        }
    }

    public static short valueOf(Short s) {
        return valueOf(s, (short)0);
    }

    public static short valueOf(Short s1, short s2) {
        if (null == s1) {
            return s2;
        } else {
            return s1.shortValue();
        }
    }

    public static int valueOf(Integer i) {
        return valueOf(i, 0);
    }

    public static int valueOf(Integer i1, int i2) {
        if (null == i1) {
            return i2;
        } else {
            return i1.intValue();
        }
    }

    public static long valueOf(Long l) {
        return valueOf(l, 0L);
    }

    public static long valueOf(Long l1, long l2) {
        if (null == l1) {
            return l2;
        } else {
            return l1.longValue();
        }
    }

    public static float valueOf(Float f) {
        return valueOf(f, 0.0f);
    }

    public static float valueOf(Float f1, float f2) {
        if (null == f1) {
            return f2;
        } else {
            return f1.floatValue();
        }
    }

    public static double valueOf(Double d) {
        return valueOf(d, 0.0d);
    }

    public static double valueOf(Double d1, double d2) {
        if (null == d1) {
            return d2;
        } else {
            return d1.doubleValue();
        }
    }
}

See Also