开发者

A strange error in java generic

开发者 https://www.devze.com 2023-02-04 18:30 出处:网络
This is ok: Class<? extends String> stringClass = \"a\".getClass(); But this gets error: <T> void f(T obj) {

This is ok:

Class<? extends String> stringClass = "a".getClass(); 

But this gets error:

<T> void f(T obj) {
    Class<? extends T> objClass = obj.getClass();
}

I know I can cast it like:

<T> void f(T obj) {
    @SuppressWarnings("unchecked")
    Class<? extends T> objClass = (Class<? extends T>) obj.getClass();
}

But why the previous error? Will the next release of Java 7 will support suc开发者_运维技巧h usage?

EDIT

I have no problem dealing with type erasure, and just put the extra SuppressWarnings whenever my code is right but the compiler just doesn't support of it.

The question behind is, SuppressWarnings is just not enough. When T.getClass() => ? extends T is obviously intuitive, why should I have to suppress any warning?

As you have already seen in the javadoc:

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

The Object.getClass() is a special case when we talk about Java generics. It's different to the declared form, which just returns Classs<?>. I don't know how IDE like Eclipse stuff is implemented, in Eclipse IDE, the completion will generate Class<? extends |X|> instead of Class<?> for you. I guess there maybe some magic stuff in the Object class, to make the base type, which ? extends from, be different between derived types. So, it's just not possible to write your own Object class in Java language, which will return something like ? extends |X| where |X| is this.getClass(). But the Java Object does, because it's a special case.


Class represents a class in the JVM, not the Java language. So for List<String> the actual Class is Class<List>. In your example, if T was List<String>, getClass will return Class<? extends List> as described in the Object.getClass API docs.


Looking at the Javadoc, this is the signature for Object.getClass():

public final Class<?> getClass()

It goes on to say

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.

Class literals are defined in section 15.8.2 in the JLS.

In the first case, the static type of "a".getClass() is String, so the return type of getClass() is Class<? extends String>. In the second case, the erasure of obj is Object, so the return type is Class<? extends Object> which is really just Class<?>

You might be wondering why "a".getClass() does not return Class<String>. After all, the compiler "knows" that "a" is a String. The reason is this: when they made Class generic, they decided that Class<T> should represent a class of type T, not a class of some subtype of T. The signature of getClass falls from that decision and the consequences of inheritance:

<T> Class<? extends Number> getType(Number obj) {
   return obj.getClass();
}

If you call getType(Long.valueOf(123) it will return Class<? extends Number>. The expression "a" is an object of type String. For consistency, "a".getClass() must return Class<? extends String>


It's because of type erasure. In the first case, you have an actual concrete type in hand, so .getClass() succeeds. In the second case, the actual type of obj is erased at runtime, so .getClass() can't do better than Object.

Reified generics would fix this. According to Alex Miller's blog, this feature will not be present in Java 7. That question was also asked here on StackOverflow.


The declared return type for getClass is Class<?> so, unfortunately, the compiler only knows that getClass will return a capture of <? extends Object> which is not the same as capture of <? extends T>.

From the javadocs:

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.

which is why the version with the explicit cast works.

In your first example the compiler knows the static type of the object is String so can do the capture as you would expect.

Note that you can do the following:

public static <T extends Number> void f(T obj) {
    Class<? extends Number> objClass = obj.getClass();
}

because we can guarantee that the static type of T is a subclass of Number.


<? extends T> means a specific subtype of T,

For example, if T is Exception, <? extends T> can be RuntimeException or SQLException. You will never know if T.getClass() is RuntimeException or SQLException. So explicit cast is needed.

0

精彩评论

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