Architecture & DesignHow To Use Autoboxing and Unboxing in Java Programming

How To Use Autoboxing and Unboxing in Java Programming

Since JDK version 5, Java provides two features, called autoboxing and auto-unboxing. They typically mean automatic conversion between primitive type values to its corresponding wrapper classes. This idea simplified coding of several algorithms, removing the trouble of explicit boxing and unboxing of values. The conversion is implicit. It blends to the natural way of writing Java code to the extent of almost getting unnoticed. Rest assured the principles behind it are intriguing and worth knowing. This article attempts to elaborate on autoboxing, auto-unboxing, and its allied ideas as applied to principle of Java programming.

Java Primitive and Composite Types

Java is a statically typed language. Therefore, every variable and expression have a type that must be known before compilation. Like in any statically typed language, the primitive data types form the basic building blocks of representing data. They are called primitive perhaps because these data type components are atomic because they cannot be decomposed any further, scalar because they represent unidimensional values, and basic because they represent simpler data. In contrast to atomic or primitive data types, there are other data types that are non-primitive or composite. The non-atomic or composite types represent a complicated structure. They are typically a composition of one or more varieties of primitive type for the purpose of representing meaningful data.

Java provides eight built-in names for primitive data types: byte, short, int, long, char, Boolean and double, according to the type of value they can represents and store. For example, an int can store only integer numeric values that are only within a certain range of 32-bit. Similarly, other data types have their specific size and range of stored values. Here is a simple program to illustrate the size and range of each data type in Java.

public class TestPrimitive{
   public static void main(String[] args) {
      System.out.printf("%-10s%20s%30s%30sn","DATA TYPE","SIZE",
         "MINIMUM VALUE","MAXIMUM VALUE");
      System.out.println("------------------");
      System.out.printf("%-10s%20s%30d%30dn","BYTE",(Byte.BYTES*8)
         +"-bit",Byte.MIN_VALUE,Byte.MAX_VALUE);
      System.out.printf("%-10s%20s%30d%30dn","SHORT",
         (Short.BYTES*8)+"-bit",Short.MIN_VALUE,Short.MAX_VALUE);
      System.out.printf("%-10s%20s%30d%30dn","INTEGER",
         (Integer.BYTES*8)+"-bit",Integer.MIN_VALUE,
         Integer.MAX_VALUE);
      System.out.printf("%-10s%20s%30d%30dn","LONG",(Long.BYTES*8)
         +"-bit",Long.MIN_VALUE,Long.MAX_VALUE);
      System.out.printf("%-10s%20s%30s%30sn","CHARACTER",
         (Character.BYTES*8)+"-bit","'u0000'","'uffff'");
      // System.out.println("Boolean Size "+Boolean.;
      System.out.printf("%-10s%20s%30e%30en","FLOAT",
         (Float.BYTES*8)+"-bit",Float.MIN_VALUE,Float.MAX_VALUE);
      System.out.printf("%-10s%20s%30e%30en","DOUBLE",
         (Double.BYTES*8)+"-bit",Double.MIN_VALUE,
         Double.MAX_VALUE);
      System.out.println("BOOLEAN t (*) unspecified");
      System.out.println("------------------");
   }
}

The eight data types and their values
Figure 1: The eight data types and their values

Anything else or other than the preceding primitives are a non-primitive or composite type. Note that composite types are nothing but a collection of primitives. For example, the String data type we use in Java is actually a class which is a collection of char data types. Therefore, in a way, any class definition is a composite data type, so are arrays, enums, and so forth. They are all composite types.

Wrapper Classes

Now, the point is that Java is a complete object-oriented language. As a result, anything and everything in Java must be designated as a class so that we are able to instantiate it to create an object reference type as and when required. This is not just a philosophical need; it has a practical merit as well. The primary advantage of a class is that it encompasses not only the data as its property but also methods that enhance or modify the behaviour of the property which may be designated and exposed as the functionality of the class. This idea can be exploited to the advantage of the programmer in a way where the seemingly simple looking primitive types are geared up with additional features. These features actually wrap up the naked primitives in the name of class which otherwise are nothing but atomic units. For example, the Integer class wraps up the int primitive type. Similarly, there are wrappers called Float for float, Double for double, Character for char, Byte for byte, Short for short, Long for long primitive, and Boolean for the Boolean type.

There is the ninth primitive, called void, and its corresponding wrapper class is Void. According to the Java API documentation, the Void class is an un-instantiable placeholder class to hold a reference to the Class object representing the Java keyword void. It seems it is a namesake wrapper class because the class contains no method of its own. It is rarely used, perhaps, so that you cannot say—OOPs! Java, void primitive does not have a wrapper class 🙂

Conversion

Now, we have primitive types and their corresponding wrapper classes, fine! But what about the conversion between primitives to wrapper and vice versa. Because primitive types are the building blocks of Java code, there must be some convenient conversion techniques between primitives and wrappers. Guess what, there actually is. Let’s see down the line.

Boxing

The technique of converting an expression of primitive type as an expression of a corresponding reference type is called boxing conversion. This means we wrap them or box them into a reference type.

Unboxing

It is a reverse process of boxing. It treats an expression of a reference type as an expression of a corresponding primitive type. This means we unwrap it or unbox it into a primitive type.

Autoboxing and Auto-unboxing

With autoboxing and auto-unboxing, the capability the conversion between primitive types and objects occurs automatically. Autoboxing converts primitive types into wrapper objects and auto-unboxing takes place when an object is to be converted into a primitive type. This auto conversion occurs according to the need. For example, it occurs when an argument is passed to a method or when a value is returned from the method. Consider the following example.

public class TestAutoBoxing{
   public static int addNumbers(Integer x, Integer y){
      return (x+y);
   }
   public static void main(String[] args) {
      Integer result = addNumbers(10, 20);
      System.out.println(result);
   }
}

Note that the method addNumbers takes two Integer reference type parameters, but returns a primitive type int value. In the main method, we have passed two intvalues—10 and 20. Now, observe that the number of conversion occurring at different points in the code. For example, the int value 10 and 20 is auto boxed into a reference type by the addNumbers method whereas the return value is actually a primitive type but again is converted before storing into the result reference type. Thus, all the boxing and unboxing are taking place automatically without the programmer’s explicit intervention.

The capability of autoboxing and auto unboxing also be seen with expressions. Consider the following example.

Integer ref_x, ref_y;
int i;
ref_x=10;

ref_x++;

In the above post-increment statement, ref_x is unboxed first, then incrementing is performed, and then finally re-boxed again back into ref_x. All this is happening automatically. Consider another example:

ref_y = ref_x + (ref_x * 3);

In this case, also the ref_x is unboxed first, the expression is evaluated, and then finally re-boxed again before assigning the value back to ref_y.

Now, what happens with mixed types of values? The idea is pretty much the same. Consider this example:

Integer i_ref=10;
Double d_ref=5.6;

d_ref = i_ref - d_ref;

Here, also, the evaluation is performed after unboxing and then re-boxed again before storage. If you have been thinking about what if the expression was this instead:

i_ref = i_ref - d_ref;

Now, this a lossy conversion. The fractional value resulted from the evaluation is lost because we are storing the value in an integer variable. This falls into the Java concepts called narrow primitive conversion and widening primitive conversion.

A narrowing primitive conversion may lose information about the overall magnitude of a numeric value and also may lose precision and range.

A widening primitive conversion does not lose information about the overall magnitude of a numeric value in the following cases, where the numeric value is preserved exactly.

Refer to Java Language Specification or more details on this.

With character and Boolean values, autoboxing and auto unboxing occurs in the following way:

Boolean b_ref = true;  // The value 'true' is boxed before storing
Boolean b = b_ref;     // b_ref is unboxed before assignment

Similarly, for the character primitive type:

Character c_ref = 'A';    // the value 'A' is boxed before storing
char c = c_ref;           // c_ref is unboxed before assignment

Utility of Autoboxing and Auto-unboxing

Autoboxing and auto-unboxing not only offer a convenient way to write code but also allays the chance of incorporating unintended errors in the code. Prior to this automatic capability incorporated in Java, the procedure was to manually unbox and box the primitive expressions because this was done with the help of methods that were evaluated only at the runtime. This makes it vulnerable to infusing bugs into the code. For example,

Long l_ref = 83775252436637L;
long l = l_ref.byteValue();
System.out.println(l);

The l_ref is manually unboxed but erroneously unboxed to a byte value before storing into a long type variable. The result is the wrong value is printed! This is a simple example of how manually boxing and unboxing may infuse bugs into the code.

Conclusion

Although the idea of autoboxing and auto-unboxing is great and convenient to use all the time, one should be cautious when using the primitive wrapper reference types in the sense that these wrapper classes are not exchangeable with primitive types. Primitive types are simple atomic, and their corresponding wrapper classes are heavy. Also, any conversion, even an automatic one, always needs some extra processing. The crux of the matter is that wrapper classes should be used as sparingly as possible to lessen the burden of extra processing. Hail! the primitive types; wrap them up only when it is needed the most.

References

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories