23

Is there a way to get Class object from the type variable in Java generic class? Something like that:

public class Bar extends Foo<T> {
    public Class getParameterClass() {
        return T.class; // doesn't compile
    }
}

This type information is available at compile time and therefore should not be affected by type erasure, so, theoretically, there should be a way to accomplish this. Does it exist?

Alexander Temerev
  • 2,654
  • 3
  • 27
  • 34

3 Answers3

26

This works:

public static class Bar extends Foo<String> {
  public Class<?> getParameterClass() {
    return (Class<?>) (((ParameterizedType)Bar.class.getGenericSuperclass()).getActualTypeArguments()[0]);
  }
}
sfussenegger
  • 35,575
  • 15
  • 95
  • 119
  • -1. It simply can't. http://java.sun.com/docs/books/tutorial/java/generics/erasure.html – Roman May 10 '10 at 08:32
  • 5
    @Roman It can't? Simply try it. Downvoting without actually doing so while insisting on your wrong answer is ridiculous anyway. – sfussenegger May 10 '10 at 08:33
  • 2
    Roman: I suggest you actually try this. Yes, types get erased and cannot be relied on at compile time, but you can use runtime reflection to see what those types are. It comes with a few gotchas, but broadly works. – GaryF May 10 '10 at 08:34
  • 6
    @GaryF: It's really more the other way round: there is no information about type parameters in *instances* of generic types, but there *is* information in compile-time entities such as classes and fields. – Michael Borgwardt May 10 '10 at 08:41
  • 5
    Alexander, what exactly works? In your example there is a type variable `T`, while in this snippet it's `String` instead. You already know that it's `String`, what the point of all these manipulations? Did you manage to solve the `T.class` problem mentioned in the question? – yegor256 Feb 05 '12 at 23:34
  • There's confusion because you can't(?) do what the OP coded, so this responder assumed he meant `T` was an actual type, and not the usual type placeholder actually coded up. Since the OP replied above, must be the case. – Colin M. Jul 12 '12 at 15:50
  • @Skywalker5446 well, the problem is that the OP's code wouldn't compile. T isn't defined as Bar isn't parameterized itself. Therefore I replaced T with String for demonstration purpose. However, you're right that the above method would cause a ClassCastException if Bar would be changed to Bar. – sfussenegger Jul 18 '12 at 12:58
6

The code snippet is a bit confusing. Is T a type parameter or a class?

public static class Bar extends Foo<String> {
    public Class<?> getParameterClass() {
        return (Class<?>) (((ParameterizedType)Bar.class.getGenericSuperclass()).getActualTypeArguments()[0]);
    }
}

public static class Bar2<T> extends Foo<T> {
    public Class<?> getParameterClass() {
        return (Class<?>) (((ParameterizedType)Bar2.class.getGenericSuperclass()).getActualTypeArguments()[0]);
    }
}


public static void main(String[] args) {
    System.out.println(new Bar().getParameterClass());
    System.out.println(new Bar2<Object>().getParameterClass());
}

Actually the second println will cause an exception.

tkr
  • 1,331
  • 1
  • 9
  • 27
3

This code works for derived classes as well:

import java.lang.reflect.ParameterizedType;

public abstract class A<B> 
{
    public Class<B> getClassOfB() throws Exception 
    {
        ParameterizedType superclass = (ParameterizedType) getClass().getGenericSuperclass();

        return (Class<B>) superclass.getActualTypeArguments()[0];
    }
}

snagged from here: https://stackoverflow.com/a/4699117/26510

Community
  • 1
  • 1
Brad Parks
  • 66,836
  • 64
  • 257
  • 336