Getting T.class despite Java's type-erasure

27,147

Solution 1

You need to explicitly pass the class into the constructor (and store it yourself).

private final Class<T> clazz;

PropertyImplementationBinder(Class<T> clazz){
    this.clazz = clazz;
}

public Class<T> getInterfaceClass() {
    return clazz;
}

Solution 2

You can get the actual type arguments for a generic superclass of a class. This blog post explores the possibilities presented by this, including a nice little trick using trivial anonymous inner classes. To quote directly:

It turns out that while the JVM will not track the actual type arguments for instances of a generic class, it does track the actual type arguments for subclasses of generic classes. In other words, while a new ArrayList<String>() is really just a new ArrayList() at runtime, if a class extends ArrayList<String>, then the JVM knows that String is the actual type argument for List's type parameter.

Solution 3

Contrary to what is widely accepted and rarely known type erasure can be avoided, which means that the callee do have the ability to know which generic parameters were employed during the call.

Please have a look at: Using TypeTokens to retrieve generic parameters

The article also talks about the experiences of our users with the technique. In a nutshell, we ended up falling back to the...

Conventional and widely used technique: "Pass class types in constructors"

Solution 4

Btw. The example static method getType in article from @Richard Gomes has two errors. it should go like this:

static public Class<?> getType(final Class<?> klass, final int pos) {
    // obtain anonymous, if any, class for 'this' instance
    final Type superclass = klass.getGenericSuperclass();

    // test if an anonymous class was employed during the call
    if ( !(superclass instanceof ParameterizedType) ) {
            throw new RuntimeException("This instance should belong to an anonymous class");
    }

    // obtain RTTI of all generic parameters
    final Type[] types = ((ParameterizedType) superclass).getActualTypeArguments();

    // test if enough generic parameters were passed
    if ( pos >= types.length ) {
            throw new RuntimeException(String.format("Could not find generic parameter #%d because only %d parameters were passed", pos, types.length));
    }

    if (!(types[pos] instanceof Class<?>)) {
            throw new RuntimeException("Generic type is not a class but declaration definition(all you get is \"[T]\") " + types[pos]);
    }
    // return the type descriptor of the requested generic parameter
    return (Class<?>) types[pos];
}

Unfortunately it's still not the magic bullet because it works if you have in code explicitly

getType(new SomeObject<String>(){}.class, 0) // you get String.class

but if you call this on something like

getType(new SomeObject<T>(){}.class, 0) // you get T as TypeVariable<D> and not actuall class of it

Just name T.

Share:
27,147
Kaleb Pederson
Author by

Kaleb Pederson

Software Craftsman, Husband, Father.

Updated on July 09, 2022

Comments

  • Kaleb Pederson
    Kaleb Pederson almost 2 years

    I'm trying to bind an interface to its implementation as read from a configuration file so that I can feed it to my IoC container. Here's roughly what I'm trying to do:

    public class PropertyImplementationBinder<T> {
        // ...
        public Class getInterfaceClass() {
            return T.class; // OR Class<T>, note T is not newable
        }
        public Class getImplementationClass() {
            return /* read config file to get implementation class */;
        }
    }
    

    Is it somehow possible to get T.class?

  • Kaleb Pederson
    Kaleb Pederson over 14 years
    I think that should have been obvious to me... but it wasn't. Thank you!
  • durilka
    durilka over 12 years
    well, that's creepy but amazing (let this be last fascination comment). i actually believe this should be the right answer with remark "you can also pass class in constructor"
  • hansvb
    hansvb about 12 years
    The language could have done it for us, if it had been designed for it in version 1. It is a backwards compatibility thing. There was only so much that good be done by bolting this on to Java 5 without breaking old code.
  • Richard Gomes
    Richard Gomes almost 12 years
    @durilka: Thanks a lot for your feedback
  • Erin Drummond
    Erin Drummond over 11 years
    Link appears to be broken... I know that Google's GSON library uses this approach, and I wanted to read an article on how it works
  • Andy
    Andy over 10 years
    No, you can also find parameterized type information from Class.getGeneric*(), and Method.getGeneric*().
  • Andy
    Andy over 10 years
    Everyone look below; in many cases it is possible to do without passing in the class.
  • Andy
    Andy over 10 years
    In the second example, it may still be possible to find the actual type parameter for T, if the TypeVariable.getGenericDeclaration() is a Class. It takes a lot of work to full the actual type arguments in any case (especially if they're defined by a superclass, there are multiple type parameters, etc. but I've written code to do it.
  • okigan
    okigan about 10 years
    It is possible see answers above.
  • GuiSim
    GuiSim almost 10 years
    But then the developer knows as well since the class has a static T. It won't change at runtime.
  • Eloff
    Eloff over 9 years
    @ErinDrummond fixed with link to web.archive.org