3

Simply:

public static class MyClass<T> {
    // i don't want to keep an instance of T, if it is not necessary.
    // and it is not nice, not neat.

    // Or, let's say, the member are in the form of :
    ArrayList<T> mArrayList = new ArrayList<T>();
    // the problem of getting the generic type parameter is still present.
}

@Test
public final void test() {
    MyClass<Integer> myObject = new MyClass<Integer>();
    getParamType( myObject );
}

private static final <T> void getParamType(final MyClass<T> _myObject) {
    System.out.println(_myObject.getClass().getTypeParameters()[0]);    // T
    System.out.println(((T) new Object()).getClass());                  // class java.lang.Object
}

How to let the code print class java.lang.Integer?

i know quite a few of stackoverflow threads are asking (and answering) about this. Yet they couldn't solve this question.

  • i don't know why some need to call getGenericSuperclass() - as there is no inheritance involved in this simple case.
  • And i can't cast it to ParameterizedType as well.

.

System.out.println((ParameterizedType) _myObject.getClass());
// Compile Error: Cannot cast from Class<capture#11-of ? extends TreeTest2.MyClass> to ParameterizedType

System.out.println((ParameterizedType) _myObject.getClass().getGenericSuperclass());
// Runtime Exception: java.lang.ClassCastException

Based on @Thomas's guide, i have found a work-around way to get class java.lang.Integer.

First, we create an anonymous (it need to be anonymous) sub-class of MyClass<T> in the testing code. (Which is weird. Why it only support sub-classes?)

@Test
public final void test() {
    MyClass<Integer> myObject = new MyClass<Integer>() {};  // Anonymous sub-class
    getParamType( myObject );
}

Then we can use the getGenericSuperclass() method to get a Type then cast it to ParameterizedType and afterwards uses getActualTypeArguments():

private static final <T> void getParamType(final MyClass<T> _myObject) {
    System.out.println( ((ParameterizedType) _myObject.getClass().getGenericSuperclass()).getActualTypeArguments()[0] );
}

It perfectly prints class java.lang.Integer.

This is not-so-good because the testing codes should simulate the actual situation, where users most likely won't keep creating meaningless sub-classes.

This approach is based on the idea of the TypeReference class. But i don't really know how to use it. I have tried class MyClass<T> extends TypeReference<T>. But i still have to create sub-class of MyClass<T> to have TypeReference.getType() prints class java.lang.Integer.

Please help, and thanks for any inputs, as the best approach is not here yet.


A further question based on the above workaround: Why only anonymous sub-class works?

public static class SubMyClass<T> extends MyClass<T>{}

@Test
public final void test() {
    MyClass<Integer> myObject = new MyClass<Integer>() {};  // Anonymous sub-class
    getParamType( myObject );               // class java.lang.Integer

    MyClass<Integer> mySubObject = new SubMyClass<Integer>();   // named sub-class
    getParamType( mySubObject );            // T
}

(MyClass and getParamType() unchanged.)

midnite
  • 4,919
  • 7
  • 34
  • 51

4 Answers4

3

This is sort of difficult, because Java deliberately can't do that ("type erasure").

The work-around is called super type tokens. There are also some threads on SO about that (like this one or that one).

Community
  • 1
  • 1
barfuin
  • 14,995
  • 9
  • 77
  • 120
  • Thanks Thomas! The `TypeReference` class is new to me. Can i interpret why the `TypeReference` can get its `T` because (1) it's abstract, thus force inheritance, and (2) only `Type java.lang.Class.getGenericSuperclass()` can be casted to `ParameterizedType`, thus we can use `getActualTypeArguments`? – midnite Aug 20 '13 at 16:10
  • If there is a method like `Type java.lang.Class.getGeneric_____class()` where we can do `((ParameterizedType) getClass().getGeneric____class()).getActualTypeArguments()` would be great, isn't it? Btw why Java have only the "get super" method that returns a `Type`? – midnite Aug 20 '13 at 16:12
  • Back to the original question. How to get `class java.lang.Integer` with the help of `TypeReference`? By `new TypeReference>() {}.getType()` i can get `java.util.List` (but this is not useful, as it is not getting from the object). Then i let `class MyClass extends TypeReference` and by `myObject.getType()` i get only `T`. Thanks for guided me the way. Please help me to go a bit further. – midnite Aug 20 '13 at 16:24
2

When you have a question like this, you should ask yourself, how would you do it without Generics? Because any Generics program can be converted into an equivalent program without Generics. (This conversion is called type erasure.) So if you cannot do it without Generics, you cannot do it with Generics either.

Your program without Generics looks like this:

public static class MyClass {
    ArrayList mArrayList = new ArrayList();
}

@Test
public final void test() {
    MyClass myObject = new MyClass();
    Integer result = getParamType( myObject ); // how would you implement getParamType()?
}
newacct
  • 110,405
  • 27
  • 152
  • 217
  • If there is no generics, how can we get the generics? – midnite Aug 20 '13 at 22:12
  • @midnite: It's not about the generics. It's about the logic of the program. For example, if you had made the constructor of `MyClass` take a parameter of type `Class` which represents the class, and you store it in the class, then after erasure, you would see that you're doing `MyClass myObject = new MyClass(clazz);`, and you are storing a variable of type `Class` inside the class. From this you can see clearly why that works and your approach doesn't work. – newacct Aug 20 '13 at 22:44
  • Thanks for reply. Keeping a `Class` as a member field, i understand why it works. But i don't understand the opposite. Just like, having a member field `public T mField;`, we can also retrieve the `T` from `mField.getClass()` afterwards. – midnite Aug 20 '13 at 23:06
  • Moreover, why there is only `getGenericSuperclass()` but not `getGenericThisclass()`? This seems not logical. – midnite Aug 20 '13 at 23:07
  • 1
    @midnite: Generics is just a compile-time type checking thing. It is an illusion. It doesn't really "exist". You are asking for the type parameter because you see it in this illusion. I'm showing you what it is when you peel away the illusion, to show you that what you are looking at doesn't exist, and you need something else. The most important thing to remember is: When you add generics to a piece of code, it NEVER gives you the magical ability to do something that you couldn't do before Generics. That's why it's instructive to ask yourself how you would do it without Generics. – newacct Aug 20 '13 at 23:18
  • i like your idea. This is inspiring. – midnite Aug 20 '13 at 23:30
  • ok... i accept that Generics is an illusion. And i somehow can imaging Generics is a compile-time stuff. But... for Reflections, it doesn't obey the rule of the game, does it? – midnite Aug 20 '13 at 23:40
  • @midnite: Yes, well, reflection is for retrieving the metadata of the *declarations*, e.g. the fields and methods and their types, and the superclasses, etc. And so yes, you can retrieve generics in declarations (because it needs to be accessible in order for the compiler to type-check, since Java classes can be compiled separately). But generics in declarations is not really what your question is about. – newacct Aug 22 '13 at 10:41
1

Java has a misguided feature called Type Erasure that specifically prevents you from doing that.

Generic parameter information does not exist at runtime.

Instead, you can manually accept a Class<T>.

SLaks
  • 800,742
  • 167
  • 1,811
  • 1,896
0

To learn the value of T you'll need to capture it in a type definition by subclassing MyClass:

class MyStringClass extends MyClass<String> {}

You can also do this with an anonymous class if you want:

MyClass<String> myStringClass = new MyClass<String>{}();

To resolve the value of T, you can use TypeTools:

Class<?> stringType = TypeResolver.resolveRawArgument(MyClass.class, myStringClass.getClass());
assert stringType == String.class;
Jonathan
  • 5,298
  • 33
  • 46