Why is the class of instance of generic type not the same as the generic type class?

128 views Asked by At

EDIT: even more simple example:

public <T> void shouldBeAbleToGetClassOfT(T t) {
    Class<T> tClass;

    // OK, but shows "unchecked cast" warrning.
    tClass = (Class<T>) t.getClass();

    // Compilation error!
    tClass = t.getClass();
}

Incompatible types.

Required: Class<T>

Found: Class<capture<? extends java.lang.Object>>

I am a little confused about the type erasure in the example below:

public static class Example<T> {
    private final T t;

    public Example(final T t) {
        this.t = t;
    }

    public <U extends T> void test(Consumer<T> consumer, U u) {
        // OK, but shows "unchecked cast" warrning.
        consumer.accept((T) t.getClass().cast(u));

        // OK, but shows "unchecked cast" warrning.
        consumer.accept(((Class<T>)t.getClass()).cast(u));

        // Compilation error!
        consumer.accept(t.getClass().cast(u));
    }
}

The error in question is:

Error:(21, 46) java: incompatible types: java.lang.Object cannot be converted to T

What exactly is happening here?

Is the .getClass() return value erased? Why?

What is the best way to handle this error?


EDIT: here is a little more complex use case, more closely related to my problem:

public class A<T> {
    private final T t;

    public A(final T t) {
        this.t = t;
    }

    public void printClass() {
        // OK, but shows "unchecked cast" warrning.
        B<T> b = new B<>((Class<T>) t.getClass());

        // Compilation error!
        B<T> b = new B<T>(t.getClass());

        b.printClass();
    }
}

public class B<T> {
    private final Class<T> t;

    public B(final Class<T> t) {
        this.t = t;
    }

    public void printClass() {
        System.out.println(t);
    }
}
1

There are 1 answers

8
user2357112 On BEST ANSWER

From the docs for getClass:

The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called.

The static type of t is T, and the erasure of that is Object. That means that t.getClass() has static type Class<? extends Object>, not Class<? extends T> as you might expect.

Since t.getClass() has static type Class<? extends Object>, the compiler only knows that t.getClass().cast(u) is an Object, not a T. That means you can't pass it to consumer.accept.