getClass() of a Generic Method Parameter in a Java

2019-03-27 01:56发布

The following Java method fails to compile:

<T extends Number> void foo(T t)
{
    Class<? extends T> klass = t.getClass();
}

Error received is: Type mismatch: cannot convert from Class<capture#3-of ? extends Number> to Class<? extends T>

Can someone explain why Class<? extends T> is invalid, but Class<? extends Number> is fine?

Javadoc says:

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. For example, no cast is required in this code fragment:

Number n = 0;
Class<? extends Number> c = n.getClass(); 

2条回答
小情绪 Triste *
2楼-- · 2019-03-27 02:09

It is kind of silly. It would have been better, for most use cases, if x.getClass() returns Class<? extends X>, instead of the erased Class<? extends |X|>.

The erasure is the cause of loss of information, making your code, apparently safe, fail to compile. t.getClass() returns Class<? extends |T|>, and |T| = Number, so it returns Class<? extends Number>.

The erasure (mandated by the language spec) is to maintain theoretical correctness. For example

List<String> x = ...;
Class<List> c1 = x.getClass(); // ok
Class<List<String>> c2 = x.getClass(); // error

Although c2 seems very reasonable, in Java, there is really no such class for List<String>. There is only the class for List. So allowing c2 would be, theoretically incorrect.

This formality created lots of problem in real world usages, where programmers can reason that Class<? extends X> is safe for their purposes, but have to cope with the erased version.

You can simply define your own getClass that returns the un-erased type

static public <X> Class<? extends X> getFullClass(X x)
    return (Class<? extends X>)(Class) x.getClass() ;

<T extends Number> void foo(T t)
{
    Class<? extends T> klass = getFullClass(t);
}
查看更多
女痞
3楼-- · 2019-03-27 02:15

Because the T class' type doesn't extend from T. Instead, it extends from Number, exactly as you have declared yourself in <T extends Number>. Simple as that.

A better question would be:

Why doesn't the following compile?

<T extends Number> void foo(T t)
{
    Class<T> class1 = t.getClass();
}

The answer to that is that the Object#getClass() returns Class<?> with an unbounded wildcard ? because the object itself is not directly aware about its generic type which is been expected in an arbitrary method.

查看更多
登录 后发表回答