40

I have a small problem in java while using genericity. I have a class A :

public class A<T>

In a method of A, I need to get the type name of T. Is there a way to find the string s using T ?

(If I create A<String> temp = new A<String>();, I want to be able to get java.lang.String at one point - I have to use genericity because one of my methods will have to return a List<T>).

This seems quite easy but I do not see how to do it.

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
ThR37
  • 3,965
  • 6
  • 35
  • 42

6 Answers6

61

You can't do this in general because of type erasure - an instance of A<String> doesn't know the type of T. If you need it, one way is to use a type literal:

public class A<T>
{
    private final Class<T> clazz;

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

    // Use clazz in here
}

Then:

A<String> x = new A<String>(String.class);

It's ugly, but that's what type erasure does :(

An alternative is to use something like Guice's TypeLiteral. This works because the type argument used to specify a superclass isn't erased. So you can do:

A<String> a = new A<String>() {};

a now refers to a subclass of A<String>, so by getting a.getClass().getSuperClass() you can eventually get back to String. It's pretty horrible though.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
37

You can get the name of the generics from the subclass. See this example. We Define a parent class like this:

public class GetTypeParent<T> {

    protected String getGenericName()
    {
        return ((Class<T>) ((ParameterizedType) getClass()
                .getGenericSuperclass()).getActualTypeArguments()[0]).getTypeName();
    }
}

We then define its child class in this way:

public class GetTypeChild extends GetTypeParent<Integer> {
    public static void main(String[] args) {
        GetTypeChild getTypeChild = new GetTypeChild();
        System.out.println(getTypeChild.getGenericName());
    }
}

You can see that in the main method, or in any instance method, I am capable to get the name of the generics type, in this case the main will print: java.lang.Integer.

Enrico Giurin
  • 2,183
  • 32
  • 30
  • 1
    ...and you get the name of the subclass (if any). Nice! – Per Lindberg Jun 17 '16 at 12:10
  • 1
    This seems clever indeed... but you have to "hard code" type `String` to get `String` back. Isn't the question about trying to find the value of generic parameters? – mike rodent Mar 18 '17 at 21:24
  • I do not think that this helps. The child class already knows the type because it is hard-coded: String. It is not useful to call the method of the parent to get it. Unfortunately it is not a useful use case and it is not what the OP asked as already mentioned by @mikerodent – Marco Altieri May 01 '17 at 17:21
  • 1
    it helps if you want to know at runtime what's the type, for instances, if you have a set of classes which inheritance from a common super class. – Enrico Giurin May 02 '17 at 18:20
  • @mikerodent no, getGenericName() returns the real type of T in the child class, in this case java.lang.Integer. I changed the example, before the T was java.lang.String, so it could be confusing. – Enrico Giurin Apr 28 '18 at 17:40
14

Short answer: Impossible.

Slightly longer answer: Once your code is compiled, the type parameters is discarded. Thus, Java cannot know what you set there. You could, however, pass the class in question to your object and operate on it:

public class Example<T> {
  private final Class<T> clazz;

  public Example(Class<T> clazz){
     this.clazz = clazz;
  }
...
}
Hannes Schneidermayer
  • 4,729
  • 2
  • 28
  • 32
Urs Reupke
  • 6,791
  • 3
  • 35
  • 49
  • This has got to be the answer, surely. Because of *erasure* (which is a term I've often heard but the workings of which I've never necessarily completely analysed in depth), you can never use a generic parameter as a "smuggled-in" extra parameter. Instead, use a regular parameter. It seems odd that this information can never be obtained, and I'm not clear whether this is a design choice or inevitable... – mike rodent Mar 18 '17 at 21:30
  • private final clazz ? what do you mean by that. There is on type for clazz. And 10 upvotes? – madhairsilence Jul 24 '19 at 11:13
  • 1
    @mikerodent that's not totally true. Answers here keep flopping back and forth between "no you can't" and "yes you can". The truth is both are right, depending on the situation. You should google reified types. Basically if you do `class Foo implements List` then you can get the generic type. Doing something like `List foo;` you cannot because of type erasure. Not all generic types are erased. It depends on your use of generics. – searchengine27 Jul 22 '21 at 14:47
  • 1
    Also to address your other point, the reason this is the case is a design choice...sort of. Basically the generic types that do get erasure are because they can only be validated at compile time by the compiler and cannot be verified at runtime at all, so there's no point in including it in the byte code. – searchengine27 Jul 22 '21 at 14:50
  • Answer copy from https://stackoverflow.com/a/6624140/659246 – GoldBishop Jul 06 '23 at 13:45
4

As is normally the case, Apache has a solution for this one with TypeUtils:

https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/reflect/TypeUtils.html

A quick example from the above question:

TypeUtils.getTypeArguments(temp.getClass(), A.class).get(A.class.getTypeParameters()[0])

Disclaimer: I did not attempt building this first, but have used this utility in a similar fashion in the past.

Hazok
  • 5,373
  • 4
  • 38
  • 48
  • 1
    Big fan of Apache Commons, had never heard of `TypeUtils`, so thanks... but this currently doesn't work for me: `A.class` is giving an error "illegal class literal for type parameter A"... just going to do some explorations/experiments with `TypeUtils` ... – mike rodent Mar 17 '17 at 09:35
  • @mike rodent This worked for me when I used it with multiple classes in the heirarchy and a varying number of types while moving through the heirarchy. Do you have an example available where it didn't work? – Hazok Mar 17 '17 at 09:39
  • Later: nope, couldn't get this to work. Not sure how to get a `ParameterizedType` in the first place. Also looked at Jonathan's answer and Enrico Guerin's. But getting the generic type direct from an instance (OP's question) seems impossible, even for Apache. Unless you can show me how! – mike rodent Mar 18 '17 at 21:07
3

Generics in Java are implemented by erasure, so no, you won't be able to get the name of the "type" which was used to create your generic collection at run-time. Also, why not just inspect the elements to know what type it belongs to?

Sanjay T. Sharma
  • 22,857
  • 4
  • 59
  • 71
  • Actually I can't because I haven't any instances of T at the beginning. The object implements an osgi Service Listener and I want to filter the bundles message with my interface name. I get the instances only after that. – ThR37 Jul 08 '11 at 12:11
1

If you're doing it in a subclass which has it's parent class defining the generic type, this is what worked for me:

// get generic type class name
String name = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].toString();

// then when you've got the name, you can make the Class<T> object
Class.forName(name.replace("class ", ""))

Reason why I couldn't do it with #getClass() instead of #toString() in the first snip is that I was always getting the "Class" class, which is useless to me.

milosmns
  • 3,595
  • 4
  • 36
  • 48