Friday, January 10, 2014

Auto-boxing and Unboxing




Oops, I'm not going to kick you. This is the second part of my last post about wrapper classes. In this post I'm gonna talk about boxing.

Auto boxing

All these features are introduced after Java version 5. In previous post we saw how to convert primitives into wrapper objects. After Java version 5 it gives us an automatic conversion of primitive data types into its equivalent  wrapper object.

Look at this code.

public class Autoboxing {
         public static void main(String[] args) {
     
               //Before Java5
               Integer x1 = new Integer(100);    //Wrap
               int y1 = x1.intValue();           //Unwrap
               y1++;                             //Use
               x1 = new Integer(y1);             //Re-wrap
  
               //After Java 5
               Integer x2 = new Integer(100);   //Wrap
               x2++;                            //Unwrap-> Use -> Re-wrap
  
               System.out.println("Before Java 5: " + y1);
               System.out.println("After Java  5: " + x2);
         }
}



So now you can see after Java 5, it is very easy to use wrapper objects in your programs.


==, != and equals()

In previous post I used == to check equality of wrapper objects. Here there is a simple code about all three things.


public class BoxingTest {

        public static void main(String args[]){
  
               Integer i1 = 127;
               Integer i2 = 127;
  
               if(i1 != i2){
                      System.out.println("i1 != i2");
               }
               if(i1 == i2){
                      System.out.println("i1 == i2");
               }
               if(i1.equals(i2)){
                      System.out.println("i1.equals(i2)");
               }
         }
}



When you run this program you can see the output as follows.

                          i1 == i2
                          i1.equals(i2)


Then lets look at following program.

public class BoxingTest {

         public static void main(String args[]){
  
               Integer i1 = 128;
               Integer i2 = 128;
  
                  if(i1 != i2){
                        System.out.println("i1 != i2");
                  }
                  if(i1 == i2){
                        System.out.println("i1 == i2");
                  }
                  if(i1.equals(i2)){
                        System.out.println("i1.equals(i2)");
                  }
         }
}



In this program I have changed one thing, 127 to 128. When you run this program you can see the output as follows and you may get confuse of that result. But don't confuse.

                          i1 != i2
                          i1.equals(i2)


You can see i1.equals(i2) is common output for these two programs. It means the equal method is looking for meaningful equality. But when I changed 127 to 128, it gives chaotic results, once i1 != i2 and then i1 == i2.

There is the answer for the question running in your mind. Java is doing this to save memory avoid creating unnecessary objects when programming.  Java says if there are following wrapper objects will always be same(==) when their primitive values are the same. There is the list.


              Boolean
              Byte
              Character ( \u0000 to \u007f )
              Short ( -128 to 127 )
              Integer ( -128 to 127 )


What will happen Primitive == Wrapper ?


First of all try this program and find the result.

public class EqualTest {

        public static void main(String[] args) {
  
                Integer i1 = 100;
                int i2 = 100;
  
                if(i1 == i2){
                         System.out.println("i1 == i2");
                }
                else{
                   System.out.println("i1 != i2");
                }
        }
}



You can see the output as "i1 == i2". In this program, the wrapper object that you used is automatically converted to primitive. Then it checks the comparison between primitive and primitive. 


Important ! 

Wrapper reference variable can be null, try this example to see what happen.

public class BoxTest2 {
 
         Integer x;
         public static void main(String[] args) {
                 BoxTest2 obj = new BoxTest2();
                 display(obj.x);
         }
 
         static void display(int i){
                 int y = 10;
                 System.out.println(i + y);
         }
}


The compilation is OK, But when you run it, it will give a NullPointerException


Boxing with Overloading

This is another use of overloading. You can use boxing with overloading. In this case we consider about three factors that can make overloading.


  • Widening
  • Auto-boxing
  • Var-args

What do you mean by Widening ?

Widening is the method of transforming a data type to a larger data type.
  • byte to short / int / long / float / double
  • short to int / long / float / double
  • int to long / float / double
  • long to float / double
  • float to double

Widening vs Boxing

Here is a simple program, try it.

public class Boxing1 {
         public static void main(String args[]){
                 int i = 10;
                 run(i);
         }
 
         static void run(Long x){
                 System.out.println("Long");
         }
 
         static void run(long x){
                 System.out.println("long");
         }
}


There you can see the output. It is "long". What about "Long" ? This is the thing behind the boxing with widening. Because JVM thinks that it is easy to widening primitive value than perform a boxing execution. So now Widening beats Boxing. Widening wins.


Old vs New ( Widening vs Var-args )

public class Boxing2 {
         public static void main(String args[]){
                 int i = 10;
                 calc(i,i);
         }
 
         static void calc(int x, int y){
                 System.out.println("OLD");
         }
 
         static void calc(int...x){
                 System.out.println("NEW");
         }
}



Var-args is a new thing in Java that is introduced after Java version 5. Click here to see the post about Var-args. Click here to go to Var-args in Java. In this kind of case compiler will choose older style first and then newer style. Then you can see Widening beats var-args.


Boxing vs Var-args

You saw widening vs boxing and widening vs var-args. Then you may think about the result of boxing vs var-args. Try this program and then you will able to see var-args is the looser.


public class Boxing3 {
         public static void main(String args[]){
                int i = 100;
                run(i,i);
         }
 
        static void run(Integer x, Integer y){
                System.out.println("boxing");
        }
 
        static void run(int...x){
                System.out.println("var-args");
        }
}



Widening reference variables

I think you can remember about Inheritance and IS-A relationships( Click here to go to OOP post ). There is a simple example and you can see the relationship between inheritance and widening.

class Fruit{
       static void eat(){
             System.out.println("This is the eat method in Fruit calss");
       }
}

public class Apple extends Fruit {
       public static void main(String[] args) {
             Apple obj = new Apple();
             obj.drink(obj);
             obj.eat();
      }
 
      static void drink(Fruit a){
             System.out.println("This is the eat method in Apple class");
      }
}


You can see that the drink( ) method needs Fruit parent and drink( ) method is in the Apple child class. It means Apple IS-A Fruit. In this example the compiler widening the Apple reference to Fruit. This widening is depend on inheritance. If there are IS-A relationship, it can be used widening. But you cannot widen one wrapper class to another one.  Look at the following example.


public class Boxing5 {
       public static void main(String args[]){
              Boxing5 b = new Boxing5();
              b.go(new Integer(10));
       }
 
       void go(Double d){
  
       }
}



In this example you can see an error in Eclipse like this.




Overloading with Widening and many Var-args


In previous examples you saw this thing with only one Var-arg. In this section it is about many var-args. Try this example to understand.


public class Boxing6 {
       public static void main(String args[]){
             Boxing6 obj = new Boxing6();
  
             int x = 10;
             obj.wide(x,x);
             obj.box(x,x);
       }
 
       void wide(long...i){
             System.out.println("long");
       }
 
       void box(Integer...I){
             System.out.println("Integer");
       }
}



This will compile well and run. You can change data types and Wrapper objects I have used in this example. Then you can see the possibilities of using many var-args with widening. You need to understand that you can combine var-args with either Widening or Boxing.