Boxing and Unboxing Conversion Tuesday, Jun 17 2008 

Boxing Conversion

Boxing conversion converts values of primitive type to corresponding values of reference type. Specifically, the following 8 conversion are called the boxing conversions:

 

  • From type boolean to type Boolean
  • From type byte to type Byte
  • From type char to type Character
  • From type short to type Short
  • From type int to type Integer
  • From type long to type Long
  • From type float to type Float
  • From type double to type Double

At run time, boxing conversion proceeds as follows:

 

  • If p is a value of type boolean, then boxing conversion converts p into a reference r of class and type Boolean, such that r.booleanValue() == p
  • If p is a value of type byte, then boxing conversion converts p into a reference r of class and type Byte, such that r.byteValue() == p
  • If p is a value of type char, then boxing conversion converts p into a reference r of class and type Character, such that r.charValue() == p
  • If p is a value of type short, then boxing conversion converts p into a reference r of class and type Short, such that r.shortValue() == p
  • If p is a value of type int, then boxing conversion converts p into a reference r of class and type Integer, such that r.intValue() == p
  • If p is a value of type long, then boxing conversion converts p into a reference r of class and type Long, such that r.longValue() == p
  • If p is a value of type float then:

    • If p is not NaN, then boxing conversion converts p into a reference r of class and type Float, such that r.floatValue() evaluates to p
    • Otherwise, boxing conversion converts p into a reference r of class and type Float such that r.isNaN() evaluates to true.

  • If p is a value of type double, then

    • If p is not NaN, boxing conversion converts p into a reference r of class and type Double, such that r.doubleValue() evaluates to p
    • Otherwise, boxing conversion converts p into a reference r of class and type Double such that r.isNaN() evaluates to true.

  • If p is a value of any other type, boxing conversion is equivalent to an identity conversion (5.1.1).

If the value p being boxed is true, false, a byte, a char in the range \u0000 to \u007f, or an int or short number between -128 and 127, then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2.

Unboxing Conversion

Unboxing conversion converts values of reference type to corresponding values of primitive type. Specifically, the following 8 conversion are called the unboxing conversions:

 

  • From type Boolean to type boolean
  • From type Byte to type byte
  • From type Character to type char
  • From type Short to type short
  • From type Integer to type int
  • From type Long to type long
  • From type Float to type float
  • From type Double to type double

At run time, unboxing conversion proceeds as follows:

 

  • If r is a reference of type Boolean, then unboxing conversion converts r into r.booleanValue()
  • If r is a reference of type Byte, then unboxing conversion converts r into r.byteValue()
  • If r is a reference of type Character, then unboxing conversion converts r into r.charValue()
  • If r is a reference of type Short, then unboxing conversion converts r into r.shortValue()
  • If r is a reference of type Integer, then unboxing conversion converts r into r.intValue()
  • If r is a reference of type Long, then unboxing conversion converts r into r.longValue()
  • If r is a reference of type Float, unboxing conversion converts r into r.floatValue()
  • If r is a reference of type Double, then unboxing conversion converts r into r.doubleValue()
  • If r is null, unboxing conversion throws a NullPointerException

A type is said to be convertible to a numeric type if it is a numeric type, or it is a reference type that may be converted to a numeric type by unboxing conversion. A type is said to be convertible to an integral type if it is an integral type, or it is a reference type that may be converted to an integral type by unboxing conversion.

Advertisements

What is Boxing? Tuesday, Jun 17 2008 

Boxing was introduced in JDK 1.5 to eliminate trivial conversions between primitives (int, boolean, etc) and corresponding wrapper objects (Integer, Boolean, etc). Boxing allows code to be a bit more concise and legible.

There are two complimentary aspects of boxing :

  • auto-boxing, which refers to automatic conversion of an int to an Integer, for example
  • auto-unboxing, which refers to automatic conversion of a Boolean to a boolean, for example

Boxing is useful when using collections. It is also useful more generally, where trivial conversions between a primitive and a wrapper object (or vice versa) are needed.

Some points to remember :

  • auto-unboxing a null object will cause a NullPointerException
  • boxing does not eliminate conversions, it merely performs them on your behalf. As a tiny performance optimization, one might explicitly avoid boxing to eliminate the small cost of these conversions.

The rules for comparison of primitives and wrapper objects are as follows.

If x and y are either both primitives, or both objects, then no boxing can occur :
 

Operation x and y  are primitives x and y are objects
x == y compare value compare identity
x.equals(y) does not compile compare value

If one item is a primitive, and the other item is a corresponding wrapper object, then boxing can occur :
 

Operation Behavior
x == y treat as two primitives, and compare value
x.equals(y) does not compile if x is a primitive ; otherwise treat as two objects, and compare value

Example

import java.util.*;

public final class BoxingExamples {

  public static final void main(String... aArgs){
    //pass Integer where int expected
    explode(new Integer(3));

    //pass literal where Boolean expected
    tellTruth(true);

    //calculate "int-erchangably" with int and Integer
    Integer integerYear = new Integer(1989);
    Integer otherIntegerYear = integerYear + 10;
    int intYear = integerYear + new Integer(15);
    log(integerYear.toString());
    log(otherIntegerYear.toString());
    System.out.println(intYear);

    /*
    * Comparison of primitives and wrapper objects using == and equals.
    *
    * When both items are of the same type :
    *             2 primitives          2 objects
    * ----------------------------------------------------------
    * ==       :  value                 identity
    * equals() :  not applicable        value
    *
    *
    * When one item is a primitive, and the other is an object :
    * ==          :  treat as two primitives
    * x.equals(y) :  treat as two objects; do not compile if x is primitive
    */
    intYear = 1880;
    integerYear = new Integer(1880);
    if ( intYear == integerYear ){
      log("intYear == integerYear: TRUE"); // yes
    }
    else {
      log("intYear == integerYear : FALSE");
    }
    if ( integerYear.equals(intYear) ){
      log("integerYear.equals(intYear): TRUE"); //yes
    }
    else {
      log("integerYear.equals(intYear): FALSE");
    }
    //does not compile "int cannot be dereferenced" :
    //intYear.equals(integerYear);

    intYear = 1881; //new value
    if ( intYear == integerYear ){
      log("intYear == integerYear: TRUE");
    }
    else {
      log("intYear == integerYear : FALSE"); // yes
    }
    if ( integerYear.equals(intYear) ){
      log("integerYear.equals(intYear): TRUE");
    }
    else {
      log("integerYear.equals(intYear): FALSE"); //yes
    }
  }

  // PRIVATE //
  private static void explode(int aNumTimes){
    log("Exploding " + aNumTimes + " times.");
  }

  private static void tellTruth(Boolean aChoice){
    //instead of if ( aChoice.booleanValue() ) {
    if ( aChoice ) {
      log("Telling truth.");
    }
    else {
      log("Lying like a rug.");
    }
  }

  private static void log(String aText){
    System.out.println(aText);
  }
}

An example run of this class :

>java -classpath . BoxingExamples
Exploding 3 times.
Telling truth.
1989
1999
2004
intYear == integerYear: TRUE
integerYear.equals(intYear): TRUE
intYear == integerYear : FALSE
integerYear.equals(intYear): FALSE

Autoboxing Tuesday, Jun 17 2008 

You can’t put an int (or other primitive value) into a collection. Collections can only hold object references, so you have to box primitive values into the appropriate wrapper class (which is Integer in the case of int). When you take the object out of the collection, you get the Integer that you put in; if you need an int, you must unbox the Integer using the intValue method. All of this boxing and unboxing is a pain, and clutters up your code. The autoboxing and unboxing feature automates the process, eliminating the pain and the clutter.

The following example illustrates autoboxing and unboxing, along with generics and the for-each loop. In a mere ten lines of code, it computes and prints an alphabetized frequency table of the words appearing on the command line.

import java.util.*;
 
// Prints a frequency table of the words on the command line
public class Frequency {
   public static void main(String[] args) {
      Map<String, Integer> m = new TreeMap<String, Integer>();
      for (String word : args) {
          Integer freq = m.get(word);
          m.put(word, (freq == null ? 1 : freq + 1));
      }
      System.out.println(m);
   }
}
 
java Frequency if it is to be it is up to me to do the watusi
{be=1, do=1, if=1, is=2, it=2, me=1, the=1, to=3, up=1, watusi=1}

The program first declares a map from String to Integer, associating the number of times a word occurs on the command line with the word. Then it iterates over each word on the command line. For each word, it looks up the word in the map. Then it puts a revised entry for the word into the map. The line that does this (highlighted in green) contains both autoboxing and unboxing. To compute the new value to associate with the word, first it looks at the current value (freq). If it is null, this is the first occurrence of the word, so it puts 1 into the map. Otherwise, it adds 1 to the number of prior occurrences and puts that value into the map. But of course you cannot put an int into a map, nor can you add one to an Integer. What is really happening is this: In order to add 1 to freq, it is automatically unboxed, resulting in an expression of type int. Since both of the alternative expressions in the conditional expression are of type int, so too is the conditional expression itself. In order to put this int value into the map, it is automatically boxed into an Integer.

The result of all this magic is that you can largely ignore the distinction between int and Integer, with a few caveats. An Integer expression can have a null value. If your program tries to autounbox null, it will throw a NullPointerException. The == operator performs reference identity comparisons on Integer expressions and value equality comparisons on int expressions. Finally, there are performance costs associated with boxing and unboxing, even if it is done automatically.

Here is another sample program featuring autoboxing and unboxing. It is a static factory that takes an int array and returns a List of Integer backed by the array. In a mere ten lines of code this method provides the full richness of the List interface atop an int array. All changes to the list write through to the array and vice-versa. The lines that use autoboxing or unboxing are highlighted in green:

// List adapter for primitive int array
public static List<Integer> asList(final int[] a) {
    return new AbstractList<Integer>() {
        public Integer get(int i) { return a[i]; }
        // Throws NullPointerException if val == null
        public Integer set(int i, Integer val) {
            Integer oldVal = a[i];
            a[i] = val;
            return oldVal;
        }
        public int size() { return a.length; }
    };
}

The performance of the resulting list is likely to be poor, as it boxes or unboxes on every get or set operation. It is plenty fast enough for occasional use, but it would be folly to use it in a performance critical inner loop.

So when should you use autoboxing and unboxing? Use them only when there is an “impedance mismatch” between reference types and primitives, for example, when you have to put numerical values into a collection. It is not appropriate to use autoboxing and unboxing for scientific computing, or other performance-sensitive numerical code. An Integer is not a substitute for an int; autoboxing and unboxing blur the distinction between primitive types and reference types, but they do not eliminate it.