Auto-boxing and assignment between primitives and wrappers.

Overview

The purpose of auto-boxing and un-boxing is to make assignment between primitives and their wrappers simpler. In most cases this works, however there are many edge cases where auto-boxing/un-boxing is not performed.

This feature was added to Java 5, however many methods were not altered to reflect this change. It is possible these methods were left for backward compatibly or it was not considered how these methods might have behaved if auto-boxing were part of the language when the method was written.

What does auto-boxing/un-boxing do for you?

Assignment in Java 1.4.2
// auto boxing.
int i = 5;
Integer i1 = new Integer(i);
Integer i2 = Integer.valueof(i); // Note: this is the method Java 5 calls not new Integer()
List<Integer> ints = new ArrayList();
ints.add(new Integer(i));
ints.add(new Integer(6));
// unboxing.
Double d = new Double(6.0);
double d1 = d.doubleValue(); // Note: this is what Java 5 called for you. It may throw a NullPointerException.
List<Double> doubles = ....
double d2 = doubles.get(1).doubleValue() + doubles.get(2).doubleValue();
Assignment in Java 5
int i = 5;
Integer i2 = i; // Note: this calls Integer.valueOf(int) not new Integer()
List<Integer> ints = new ArrayList();
ints.add(i);
ints.add(6);
// unboxing.
Double d = 6.0;
double d1 = d;
List<Double> doubles = ....
double d2 = doubles.get(1) + doubles.get(2);

What doesn't auto-boxing/un-boxing do?

Class.isAssignableFrom returns false for classes which are assignable.

Methods such as Class.isAssignableFrom() were not updated to reflect support for auto-boxing. In Java 5 you can

  • assign from a primitive to a wrapper. (new in Java 5)
  • assign from a wrapper to a primitive. (new in Java 5)
  • set a primitive field using a wrapper value via reflections.
  • a get() on a primitive field via reflections returns a wrapper object.
  • invoke a method which take a primitive value using a wrapper value, via reflections.
  • a method which returns a primitive returns a wrapper when called via reflections.

However, class1.isAssignableFrom(class2) is false for ALL cases where one class is a primitive and the other is its wrapper.

The workaround.

Create utility class which retains a map of primitive to wrapper classes.

public static boolean isAssignableFrom(Class to, Class from) {
   if (PRIMITIVE_TO_WRAPPER.get(from) == to || PRIMITIVE_TO_WRAPPER.get(to) == from)
      return true;
   return to.isAssignableFrom(from);
}
isAssignableFrom is an expensive JNI call. If it is called a lot you might cache the results you get from it

Array assignment.

You cannot assign as a array of primitive/ wrapper to the other type. Nor is there a method which will do this. e.g. System.arrayCopy() does not do this for you.

Integer[] ints = ...
int[] ints2 = ints; // doesn't compile.

double[] doubles = ...
Double[] doubles2 = doubles; // doesn't compile.

Double[] doubles3 = new Double[doubles.length};
System.arrayCopy(doubles, 0, doubles3, 0, doubles.length); // compiles, but doesn't work.

Workaround.

You have to create an array of the desired type which is the same length as the original and do a manual array copy.

Return value overriding.

In Java 5, you can give a more specific object type for a return type.

class A {
   Number random();
}

class B extends A {
   Double random(); // this is ok as Double extends Number.
}

class C extends B {
   // double signifies the return value cannot be null.
   double random(); // This doesn't work even though autoboxing double to Double is trivia and supported.
}

Workaround.

You need to have a second method with the desired return type and another to call it.

class C extends B {
   Double random() { return random2(); }
   double random2() { /* something */ }
}

Method call on primitives

int i1 = 333;
Integer i2 = i1;
String text = 333.toString(); // fails to compile
String text = i1.toString(); // fails to compile
String text = i2.toString();
String text = ((Integer)i1).toString(); // works okay.

Class intClass = i1.getClass(); // fails to compile.
Class intClass = ((Integer)i1).getClass(); // returns Integer.class.
Class intClass = int.class; // correct.

Workaround.

There are a number of occasion where an explicit cast to the wrapper type is required.

Synchronized on a primitive.

This is one thing which probably shouldn't work! In fact this does work...

synchronized("hello") {
  //  the hello lock.
}

synchronized ((Integer)1) {
  // the 1 lock. :P
}

synchronized(1) {
  // doesn't compile.
}

The first synchronized "works" because string literal all live in a constant pool so they are all the same object.

The second synchronized works because again there is a constant pool for Integers -128 to 127.

The third synchronized doesn't compile, which is probably a good thing. But for consistency I would suggest the previous two should have a warning. Perhaps an annotation which marks a class as a bad choice for a lock.

When you don't know you are using auto-boxing...

What does this print?

Object o = true ? new Integer(1) : new Double(2);
System.out.println(o);

This prints 1.0

Why? Rather than choosing Number as the type of the ?: expression, the compiler decides to un-box the values and made the type double which is auto-boxed to Double.

Object o = true ? (Number) new Integer(1) : new Double(2);
System.out.println(o);

This prints 1

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.