简体   繁体   中英

java generics runtime type

A post I found by Ian Robertson has a great utility method for determining the runtime class for a Java class type parameter. It goes a few steps beyond the quick version:

ParameterizedType pt = (ParameterizedType)this.getClass().getGenericSuperclass();
Class<?> c = (Class<?>) pt.getActualTypeArguments()[0]

Either method works like a charm for parameterized children inheriting from an abstract parameterized parent ( class Foo<T> extends Bar<T> ), or an anonymous instance of a parameterized abstract class ( new Foo<Bar>() {} ), and instantiated with a concrete type.


Where I fail is in trying to do the same for some other object instantiated via type parameter. Relevant class objects are:

public class Foo {/* code omitted for brevity */}

public class MyFoo extends Foo {/* code omitted for brevity */}

public abstract class AbstractLoader<T extends Foo> {/* code omitted for brevity */}

public abstract class AbstractReader<T extends Foo> {/* code omitted for brevity */}

public class MyReader<T extends Foo> extends AbstractReader<T> {/* code omitted for brevity */}

public class Loader<T extends Foo> extends AbstractLoader<T> {
    /* code omitted for brevity */

    public MyReader<T> getReader() {
        // Parameterized type "T" doesn't seem to carry through here
        return new MyReader<T>();
    }
}

Sample Code:

static void main(String... s) {
    Loader<MyFoo> loader = new Loader<>(); // new Loader<MyFoo>() for Java < 1.7
    MyReader<MyFoo> = loader.getReader();

    ParameterizedType pt = (ParameterizedType)loader.getClass().getGenericSuperclass();
    System.out.println("LoaderType = " + pt.getActualTypeArguments()[0]);
    // Prints: LoaderType = MyFoo

    pt = (ParameterizedType)reader.getClass().getGenericSuperclass();
    System.out.println("ReaderType = " + pt.getActualTypeArguments()[0]);
    // Prints: ReaderType = T
}

Intuition tells me this "should" be possible somehow, but I can't seem to discover the right answer. OTOH, this may be another example of "can't do that" due to type erasure.

You are completely misunderstanding something. There is NO DIFFERENCE between new Loader(); , new Loader<Integer>(); , and new Loader<Object>(); , etc. at runtime. It is impossible to tell them apart. Get that notion out of your head right now.

If we create a class, then the types (including generics) in the declarations about that class, including superclass, method types, field types, etc. are stored in the class file. This information can be retrieved at runtime.

So when you have new Foo<Bar>() {} , that creates an instance of a certain class (an anonymous class) which extends a generic type with a specific type parameter. It's similar to:

class SomeAnonymousClass extends Foo<Bar> {
}
new SomeAnonymousClass()

The fact that Foo<Bar> is hard-coded at compile-time as the superclass of this class, this is retrievable at runtime.

But your code is doing nothing of this sort. You did not make any subclasses of Loader .

You cannot find type parameter for an object - it is erased at run time.

If you could, then Loader loader0 , Loader<MyFoo> loader1 , and Loader<MyBar> loader2 would give different results. This difference has to be represented in runtime somehow: either in objects directly, or by referencing different classes. The first variant require additional memory for each instance and was considered unappropriate. The second requires creating classes at runtime, as class Loader itself cannot contain all possible variants of parameterized class Loader - they are unknown at compile time.

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