开发者

getClass() of a Generic Method Parameter in a Java

开发者 https://www.devze.com 2023-04-01 04:08 出处:网络
The following Java method fails to compile: <T extends Number> void foo(T t) { Class<? extends T> klass = t.getClass();

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<? exten开发者_运维百科ds 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(); 


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.


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);
}
0

精彩评论

暂无评论...
验证码 换一张
取 消