Java: What's the Difference Between Autoboxing and Casting

Java: What's the difference between autoboxing and casting?

Boxing is when you convert a primitive type to a reference type, un-boxing is the reverse. Casting is when you want one type to be treated as another type, between primitive types and reference types this means an implicit or explicit boxing operation. Whether it needs to be explicit is a language feature.

What is difference between wrapper and Auto Boxing/Unboxing in java?

Auto-boxing and auto-unboxing is just the compiler silently helping you create and use primitive wrapper objects.

For example, the int primitive type has wrapper class called Integer. You wrap and unwrap as follows:

int myInt = 7;

// Wrap the primitive value
Integer myWrappedInt = Integer.valueOf(myInt);

// Unwrap the value
int myOtherInt = myWrappedInt.intValue();

With auto-boxing and auto-unboxing, you don't have to do all that boiler-plate stuff:

int myInt = 7;

// Wrap the primitive value
Integer myWrappedInt = myInt; // Compiler auto-boxes

// Unwrap the value
int myOtherInt = myWrappedInt; // Compiler auto-unboxes

It's just a syntactic sugar, handled by the compiler. The generated byte code is the same.

Differences in auto-unboxing between Java 6 vs Java 7

It looks like the language in section 5.5 Casting Conversion of Java 7 JLS was updated in comparison to the same section in the Java 5/6 JLS, probably to clarify the allowed conversions.

Java 7 JLS says

An expression of a reference type may undergo casting conversion to a primitive type without error, by unboxing conversion.

Java 5/6:

A value of a reference type can be cast to a primitive type by unboxing conversion (§5.1.8).

The Java 7 JLS also contains a table (table 5.1) of allowed conversions (this table is not included in the Java 5/6 JLS) from reference types to primitives. This explicitly lists casts from Object to primitives as a narrowing reference conversion with unboxing.

The reason is explained in this email:

Bottom line: If the spec. allows (Object)(int) it must also be allowing (int)(Object).

Java: auto-unboxing combined with casting

This

(short)iBoxed

is a stand-alone expression that doesn't depend on its context. What you are trying to do is cast an Integer reference value to a short primitive value. That's just not a casting context that is allowed. (See the table further down in the chapter.)

Why do we use autoboxing and unboxing in Java?

Some context is required to fully understand the main reason behind this.

Primitives versus classes

Primitive variables in Java contain values (an integer, a double-precision floating point binary number, etc). Because these values may have different lengths, the variables containing them may also have different lengths (consider float versus double).

On the other hand, class variables contain references to instances. References are typically implemented as pointers (or something very similar to pointers) in many languages. These things typically have the same size, regardless of the sizes of the instances they refer to (Object, String, Integer, etc).

This property of class variables makes the references they contain interchangeable (to an extent). This allows us to do what we call substitution: broadly speaking, to use an instance of a particular type as an instance of another, related type (use a String as an Object, for example).

Primitive variables aren't interchangeable in the same way, neither with each other, nor with Object. The most obvious reason for this (but not the only reason) is their size difference. This makes primitive types inconvenient in this respect, but we still need them in the language (for reasons that mainly boil down to performance).

Generics and type erasure

Generic types are types with one or more type parameters (the exact number is called generic arity). For example, the generic type definition List<T> has a type parameter T, which can be Object (producing a concrete type List<Object>), String (List<String>), Integer (List<Integer>) and so on.

Generic types are a lot more complicated than non-generic ones. When they were introduced to Java (after its initial release), in order to avoid making radical changes to the JVM and possibly breaking compatibility with older binaries, the creators of Java decided to implement generic types in the least invasive way: all concrete types of List<T> are, in fact, compiled to (the binary equivalent of) List<Object> (for other types, the bound may be something other than Object, but you get the point). Generic arity and type parameter information are lost in this process, which is why we call it type erasure.

Putting the two together

Now the problem is the combination of the above realities: if List<T> becomes List<Object> in all cases, then T must always be a type that can be directly assigned to Object. Anything else can't be allowed. Since, as we said before, int, float and double aren't interchangeable with Object, there can't be a List<int>, List<float> or List<double> (unless a significantly more complicated implementation of generics existed in the JVM).

But Java offers types like Integer, Float and Double which wrap these primitives in class instances, making them effectively substitutable as Object, thus allowing generic types to indirectly work with the primitives as well (because you can have List<Integer>, List<Float>, List<Double> and so on).

The process of creating an Integer from an int, a Float from a float and so on, is called boxing. The reverse is called unboxing. Because having to box primitives every time you want to use them as Object is inconvenient, there are cases where the language does this automatically - that's called autoboxing.

What is the difference between autoboxing and coercion?

Coercion is another name for an implicit type cast, i.e. one mandated by the language rules, and not explicitly added by the programmer.

What is the difference between (Integer)y and new Integer(y) in java?

If all you want to do is to convert an int primitive to an Integer object,
you have four options

   Integer in = (Integer)y;         // 1 explicit cast
Integer in = y; // 2 implicit cast (autoboxing)
Integer in = new Integer(y); // 3 explicit constructor
Integer in = Integer.valueOf(y); // 4 static factory method

The most preferable way here is 2 (autoboxing). The explicit constructor (3) is the less preferable, as it might have some small performance hit.

Also, they are not strictly equivalent. Consider:

public static void main(String[] args) {
int x = 25;
Integer a = new Integer(x);
Integer b = new Integer(x);
System.out.println(a == b); // false
Integer c = Integer.valueOf(x);
Integer d = Integer.valueOf(x);
System.out.println(c == d); // true
Integer e = (Integer)x;
Integer f = (Integer)x;
System.out.println(e == f); // true
}

This is because small integers are cached (details here).



Related Topics



Leave a reply



Submit