Boxing and Unboxing in Java

The automatic conversion of primitive data types into its equivalent Wrapper type is known as boxing and opposite operation is known as unboxing.
For example, converting an int to an Integer, a double to a Double, and so on is called boxing. If the conversion goes the other way, this is called unboxing.

Boxing

Here is the simplest example of boxing:
Character ch = ‘a’;

Consider the following code:

List integers = new ArrayList<>();
for (int i = 1; i < 10; i++){
integers.add(i);
}
Although you add the int values as primitive types, rather than Integer objects, to integers, the code compiles. Because integers is a list of Integer objects, not a list of int values, you may wonder why the Java compiler does not issue a compile-time error. The compiler does not generate an error because it creates an Integer object from i and adds the object to integers. Thus, the compiler converts the previous code to the following at runtime:

List integers = new ArrayList<>();
for (int i = 1; i < 10; i++){
integers.add(Integer.valueOf(i));
}

Unboxing

Converting an object of a wrapper type to its corresponding primitive value is called unboxing.

Consider the following method:

public static int sumEven(List integers) {
int sum = 0;
for (Integer i: integers)
if (i % 2 == 0)
sum += i;
return sum;
}

Because the remainder (%) and unary plus (+=) operators do not apply to Integer objects, you may wonder why the Java compiler compiles the method without issuing any errors. The compiler does not generate an error because it invokes the intValue method to convert an Integer to an int at runtime:

public static int sumEven(List integers) {
int sum = 0;
for (Integer i : integers)
if (i.intValue() % 2 == 0)
sum += i.intValue();
return sum;
}

Overloading: Matching Methods

Primitive widening: Compiler uses the “smallest” method argument possible.

For the primitive data types, the value of a narrower data type can be converted to a value of a wider data type. This is called a widening primitive conversion.

Screen Shot 2017-04-19 at 11.16.55 AM

Let’s look at an example:
class EasyOver {
static void go(int x) { System.out.print(“int “); }
static void go(long x) { System.out.print(“long “); }
static void go(double x) { System.out.print(“double “); }
public static void main(String [] args) {
byte b = 5;
short s = 5;
long l = 5;
float f = 5.0f;
go(b);
go(s);
go(l);
go(f);
}
}
Which produces the output:int int long double
This probably isn’t much of a surprise; the calls that use byte and the short arguments are implicitly widened to match the version of the go() method that takes an int. Of course, the call with the long uses the long version of go(), and finally, the call that uses a float is matched to the method that takes a double.
In every case, when an exact match isn’t found, the JVM uses the method with the smallest argument that is wider than the parameter.

Primitive Widening vs Boxing

Now let’s add boxing into the mix:
class AddBoxing {
static void go(Integer x) { System.out.println(“Integer”); }
static void go(long x) { System.out.println(“long”); }
public static void main(String [] args) {
int i = 5;
go(i); // which go() will be invoked?
}
}
The compiler thinks that widening a primitive parameter is more desirable than performing an autoboxing operation,so the output will be long

Widening vs var-args

class AddVarargs {
static void go(int x, int y) { System.out.println(“int,int”);}
static void go(byte… x) { System.out.println(“byte… “); }
public static void main(String[] args) {
byte b = 5;
go(b,b); // which go() will be invoked?
}
}
The compiler thinks that widening a primitive parameter is more desirable than varargs,so the output will be int,int

Boxing vs var-args

class BoxOrVararg {
static void go(Byte x, Byte y)
{ System.out.println(“Byte, Byte”); }
static void go(byte… x) { System.out.println(“byte… “); }
public static void main(String [] args) {
byte b = 5;
go(b,b); // which go() will be invoked?
}
}
The compiler thinks that boxing is more desirable than varargs,so the output will be Byte, Byte.
A var-args method is more like a catch-all method and it makes most sense for catch-all capabilities to be used as a last resort.

Conclusion: So the compiler has the following preference:
Widening > Boxing > Var-args

Widening and Boxing Rules:

You CANNOT widen from one wrapper type to another. Bytes won’t widen to Shorts, Shorts won’t widen to Longs, etc.(IS-A fails.)

The key point here is that reference widening depends on inheritance, in other words the IS-A test. Because of this, it’s not legal to widen from one wrapper class to another, because the wrapper classes are peers to one another. For instance, it’s NOT valid to say that Short IS-A Integer.

You CANNOT widen and then box. (An int can’t become a Long. We cannot widen int to long and then box it to Long)

You can box and then widen. (An int can become an Object, via an Integer. int can be boxed to Integer then widen to Object)

You can combine var-args with either widening or boxing.
We can combine var-args with either widening or boxing in a method-matching scenario. Let’s take a look:
class Vararg {
static void wide_vararg(long… x)
{ System.out.println(“long…”); }
static void box_vararg(Integer… x)
{ System.out.println(“Integer…”); }
public static void main(String [] args) {
int i = 5;
wide_vararg(i,i);// needs to widen and use var-args
box_vararg(i,i);// needs to box and use var-args
}
}
This compiles and produces:
long…
Integer…

Leave a Reply

Your email address will not be published. Required fields are marked *