3

I am wondering how can I get the runtime type which is written by the programmer when using generics. For example if I have class Main<T extends List<String>> and the programmer write something like

Main<ArrayList<String>> main = new Main<>();

how can I understand using reflection which class extending List<String> is used?

I'm just curious how can I achieve that. With

main.getClass().getTypeParameters()[0].getBounds[] 

I only can understand the bounding class (not the runtime class).

Pshemo
125k26 gold badges194 silver badges280 bronze badges
asked Aug 16, 2015 at 23:58
4
  • 4
    You can't. In Java specific generic types are erased at runtime. They are compilers tool, not runtime. Commented Aug 17, 2015 at 0:05
  • As mentioned by Pshemo, this is not supported by the JVM due to type erasure. More about some work-arounds in Get generic type of class at runtime. Commented Aug 17, 2015 at 0:10
  • Are you looking to get the type arguments for any arbitrary generic type? Or for a particular type that you can change? Commented Aug 17, 2015 at 4:57
  • 1
    Thank you all, for your attention. I know that the generics are removed after compilation, but I am wondering how then ClassCastException is thrown runtime ? Sorry, if this is a stupid question, but how it knows to throws this exception if there isn't any information about classes. Thanks in advance. Commented Aug 17, 2015 at 17:11

1 Answer 1

5

As the comments above point out, due to type erasure you can't do this. But in the comments, the follow up question was:

I know that the generics are removed after compilation, but I am wondering how then ClassCastException is thrown runtime ? Sorry, if this is a stupid question, but how it knows to throws this exception if there isn't any information about classes.

The answer is that, although the type parameter is erased from the type, it still remains in the bytecode.

Essentially, the compiler transforms this:

List<String> list = new ArrayList<>();
list.add("foo");
String value = list.get(0);

into this:

List list = new ArrayList();
list.add("foo");
String value = (String) list.get(0); // note the cast!

This means that the type String is no longer associated with the type ArrayList in the bytecode, but it still appears (in the form of a class cast instruction). If at runtime the type is different you'll get a ClassCastException.

This also explains why you can get away with things like this:

// The next line should raise a warning about raw types
// if compiled with Java 1.5 or newer
List rawList = new ArrayList();
// Since this is a raw List, it can hold any object.
// Let's stick a number in there.
rawList.add(new Integer(42));
// This is an unchecked conversion. Not always wrong, but always risky.
List<String> stringList = rawList;
// You'd think this would be an error. But it isn't!
Object value = stringList.get(0);

And indeed if you try it, you'll find that you can safely pull the 42 value back out as an Object and not have any errors at all. The reason for this is that the compiler doesn't insert the cast to String here -- it just inserts a cast to Object (since the left-hand side type is just Object) and the cast from Integer to Object succeeds, as it should.

Anyhow, this is just a bit of a long-winded way of explaining that type erasure doesn't erase all references to the given type, only the type parameter itself.

And in fact, as a now-deleted answer here mentioned, you can exploit this "vestigial" type information, through a technique called Gafter's Gadget, which you can access using the getActualTypeArguments() method on ParameterizedType.

The way the gadget works is by creating an empty subclass of a parameterized type, e.g. new TypeToken<String>() {}. Since the anonymous class here is a subclass of a concrete type (there is no type parameter T here, it's been replaced by a real type, String) methods on the type have to be able to return the real type (in this case String). And using reflection you can discover that type: in this case, getActualTypeParameters()[0] would return String.class.

Gafter's Gadget can be extended to arbitrarily complex parameterized types, and is actually often used by frameworks that do a lot of work with reflection and generics. For example, the Google Guice dependency injection framework has a type called TypeLiteral that serves exactly this purpose.

answered Aug 17, 2015 at 22:51
Sign up to request clarification or add additional context in comments.

1 Comment

Stellar answer, I'd never thought about the Object value = stringList.get(0); trick before.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.