No ClassCastException is thrown inside Java generics
Asked Answered
V

4

14

Below is the first Java generics I've ever written :

public class MyClass {

    public static <T> T castToAnotherType(Object param) {
        T ret = null;
        try {
            ret = (T) param;
        } catch (ClassCastException e) {
            System.out.print("Exception inside castToAnotherType()");
        }
        return ret;
    }

    public static void main(String[] args) {
        try {
            String obj = MyClass.castToAnotherType(new Object());
        } catch (ClassCastException e) {
            System.out.print("Exception outside castToAnotherType()");
        }
    }

}

The result is "Exception outside castToAnotherType()". Why did the exception not occur inside the generic method?

Vickivickie answered 2/11, 2014 at 13:21 Comment(1)
See also #340199.Betulaceous
T
15

T is effectively erased during compilation. See here:

Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:

  • Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
  • Insert type casts if necessary to preserve type safety. Generate bridge methods to preserve polymorphism in extended generic types.
  • Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.

So your castToAnotherType gets T erased into ca. the following:

public static Object castToAnotherType(Object param) {
    Object ret = null;
    try {
        ret = (Object) param;
    } catch (ClassCastException e) {
        System.out.print("Exception inside castToAnotherType()");
    }
    return ret;
}

Which does obviously not produce any ClassCastException.

main(...) is a different story, it results into the following:

public static void main(String[] args) {
    try {
        String obj = (String) MyClass.castToAnotherType(new Object());
    } catch (ClassCastException e) {
        System.out.print("Exception outside castToAnotherType()");
    }
}

Which produces the ClassCastException when trying to cast Object to String.

Please see the Type Erasure part of the Generics tutorial.

Triplett answered 2/11, 2014 at 13:35 Comment(2)
It is much more complicated than I thought. I need to spend more time studying to fully understand this when I have time to. For now, do you have any suggestion how to fix it? For example: return null when param cannot be cast to type T?Vickivickie
@Vickivickie This is another question, please ask it separately. In short, one of the methods would be to pass the Class<T> targetClass as an extra parameter. But I'd love to see other answers. :)Triplett
N
4

Well, since the compiler erases the generic type parameters, the casting inside the method is essentially equivalent to :

    Object ret = null;
    try {
        ret = (Object) param;
    } 
    ...

which is not a problem, regardless of what you pass to your method (since any Object can be cast to Object).

However, when you try to assign that Object to a String, in your main method, the ClassCastException occurs, since Object cannot be cast to a String.

Narda answered 2/11, 2014 at 13:35 Comment(0)
L
3

All the generic types are erased in the compiled code. As far as the compiled code is concerned, castToAnotherType just returns an Object. However, your main method tries to assign it to a String, and it is not a String, so that produces a ClassCastException.

http://en.wikipedia.org/wiki/Type_erasure

Lh answered 2/11, 2014 at 13:35 Comment(0)
T
3

This is because of generic type erasure,

       T ret = null;
       try {
            ret = (T) param;
...

is translated by compiler into

       Object ret = null;
       try {
            ret = (T) param;
...
Triceratops answered 2/11, 2014 at 13:40 Comment(1)
Actually ret = (Object) param;.Hl

© 2022 - 2024 — McMap. All rights reserved.