简体   繁体   中英

Unexpected Type Erasure

I am having an issue with generics and what I presume is type erasure whilst trying to reflect some generic information.

I am using a class based on http://www.artima.com/weblogs/viewpost.jsp?thread=208860 to do the reflection, but that isn't the issue.

The code below demonstrates the issue:

private <U extends MyClass1> U doSomething(MyClass2<U> p) {
     Class<U> classIWant = null;
     for (Class<?> c : GenericTypeUtils.getTypeArguments(MyClass2.class, p.getClass()))      {
        if (MyClass1.class.isAssignableFrom(c)) {
            classIWant = c.asSubclass(MyClass1.class);
            break;
        }
    }
}

Unfortunately the line

classIWant = c.asSubclass(MyClass1.class);

Shows the following error

java: incompatible types
  required: java.lang.Class<U>
  found:    java.lang.Class<capture#4 of ? extends MyClass1>

And that's really confusing me. U extends MyClass1 and therefore U is a MyClass1.

Obviously there is some piece of type erasure I am not understanding here - can anybody help, please. BTW this is Java 1.7.0_40

Thanks

Conrad Winchester

The method asSubclass() is implemented like so

public <U> Class<? extends U> asSubclass(Class<U> clazz) {
    if (clazz.isAssignableFrom(this))
        return (Class<? extends U>) this;
    else
        throw new ClassCastException(this.toString());
}

with a return type of Class<? extends U> Class<? extends U> . Note, that your U and the U in the asSubclass method are completely unrelated type variables.

So the method asSubclass() returns a value declared with the type Class of some unknown subtype of U , where U in this case is MyClass1 .

You are trying to assign this value to a variable of type Class with some known subtype of MyClass1 . The compiler can't guarantee that they match and therefore doesn't allow compilation.

You can cast the returned value if you wish, but may end up getting ClassCastException at runtime, if the types don't match.

The Java compiler is unable to guarantee that the Class object c matches the generic type U . Because of type erasure, it is up to the compiler to guarantee type safety, and ensure that the Class coming back from c.asSubclass matches Class<U> , but it can't.

The asSubclass method returns a Class<? extends U> Class<? extends U> . In the Class class, U represents the type of Class passed into asSubclass . When you call it, that is c . But the compiler cannot guarantee that this return type matches your own declared <U extends MyClass1> . They could be different subclasses of MyClass1 .

If you can guarantee that c.asSubclass will return a Class<U> , then cast it as a Class<U> :

classIWant = (Class<U>) c.asSubclass(MyClass1.class);

You will receive an "unchecked cast" warning. This is there because the compiler cannot guarantee that is really is a Class<U> , but you are saying that it is. Be careful as this may result in a hard-to-track-down ClassCastException elsewhere, even after you return the U . Do this only if you can guarantee the type safety yourself.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM