Question

Suppose there is the following code:

@SuppressWarnings("unchecked")
public static  <T> T implicitCaster(Class<T> cls, Object o) {
    return (T) o;
}

public static <T> T reflectionCaster(Class<T> cls, Object o) {
    return cls.cast(o);
}

The code works as expected in both cases with the following exception, found in primitives:

public static void main(String[] args) {
    System.out.println(implicitCaster(int.class, 42));
    System.out.println(reflectionCaster(int.class, 42));
}

The first call works as expected but the second call throws java.lang.ClassCastException.

Is this a corner case in which autoboxing was disregarded? Or is it impossible to provide autoboxing in this case, of reflection casting? Or is there something else causing this inconsistency?

Edit: calling this code works as expected:

public static void main(String[] args) {
    System.out.println(implicitCaster(Integer.class, 42));
    System.out.println(reflectionCaster(Integer.class, 42));
}
Was it helpful?

Solution

This happens because of type erasure.

At runtime, generic type parameters don't exist.
Casting an object to a generic type parameter has no effect. (which is why you get the unchecked cast warning)

Therefore, your first line autoboxes 42 to Object to pass to the method.
The function then returns that Object, which is passed to System.out.println.


Your second call calls the cast method of the int primitive type.
This throws an exception, because objects cannot be casted to primitive types. (auto-boxing is a purely compile-time feature, so it doesn't help)

The error happens when cast() checks isInstance() to verify that the cast is valid.

The docs for isInstance() say:

Specifically, if this Class object represents a declared class, this method returns true if the specified Object argument is an instance of the represented class (or of any of its subclasses); it returns false otherwise. If this Class object represents an array class, this method returns true if the specified Object argument can be converted to an object of the array class by an identity conversion or by a widening reference conversion; it returns false otherwise. If this Class object represents an interface, this method returns true if the class or any superclass of the specified Object argument implements this interface; it returns false otherwise. If this Class object represents a primitive type, this method returns false.

(emphasis added)


Your edit works because you are no longer using a primitive type.
In both cases, the compiler autoboxes 42 so that it can be passed as an object.

The first call, as before, has no effect.
The second call verifies that the boxed integer is in fact an instance of the Integer class, then returns it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top