12

Possible Duplicate:
Why are not all type information erased in Java at runtime?

Java's generics are implemented via type erasure, so I thought it was no possible to get any information about the parameterized type at runtime. However, I found the following class in the Jackson library.

(I've simplified the class slightly for the sake of this example)

public abstract class TypeReference<T> {
    final Type _type;

    protected TypeReference() {
        Type superClass = getClass().getGenericSuperclass();
        _type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public Type getType() { return _type; }

}

The class provides access to it's parameterized type as the following test demonstrates:

void testTypeReference() {
    // notice that we're instantiating an anonymous subclass of TypeReference
    TypeReference<CurrencyDto> tr = new TypeReference<CurrencyDto>() {};
    assert tr.getType() == CurrencyDto.class
} 

This class illustrates that actual type parameters can be retrieved at runtime (using reflection), how is this consistent with the notion that Java Generics are implemented via type erasure?

Community
  • 1
  • 1
Dónal
  • 185,044
  • 174
  • 569
  • 824
  • 2
    I don't think this is a duplicate of that other question. That question asks _why_ the compiler stores that information, where as this asks for an explanation of how this fits with the notion of type erasure. – ColinD Jun 09 '11 at 17:20

3 Answers3

12

Concrete type argument information is stored in class files when it's known at compile time. For example, if you have a class with a method that returns List<String> (not List<T>!) that information will be available at runtime. Similarly, if you have a class:

public class Foo extends Bar<String> {
}

the type argument String is hardcoded in to Foo at compile time. The TypeReference class (and all similar constructs, such as TypeLiteral in Guice and TypeToken in Gson) utilizes this fact by requiring you to make an anonymous subclass of it in your code. When you do this, an actual class file will be generated with that information present, just as with the Foo example above.

For more information, see Neal Gafter's blog post here.

Type erasure more refers to the fact that you can't get information on the actual type arguments to an instance of a generic type at runtime (note that Foo, a subclass of Bar<String>, has no type variables and is not generic). For example, if you create an instance of the generic type ArrayList<E>:

List<String> foo = new ArrayList<String>();

no type information is stored in that object. So when you pass it to some other method:

public <T> T foo(List<T> list)

there's no way to find out what T is.

ColinD
  • 108,630
  • 30
  • 201
  • 202
  • Wouldn't it be more correct to say "there's no way to find out what T is, if the list is empty?" I feel a bit thick but I can't really reconcile it in my head that type information is available at run-time if the list is non-empty by doing foo.get(0).getClass(). Although I realize that we don't necessarily get the T since the objects in the list may be subclasses of T. – Marcus Junius Brutus Jun 02 '13 at 19:50
  • @Marcus Junius Brutus: `T` isn't necessarily the type of the first element. For example, the first element could be a `Double` while `T` is `Number` or `Object`. Also, keep in mind that not every generic class is a `List` or some other container where you can get an instance of `T` by calling a method. `Comparator`, for example, only has a method to which you pass instances of `T`. – ColinD Jun 03 '13 at 15:30
4

It is really quite simple: you can not get generic information from value INSTANCES, but you can get it from TYPES (classes), with some restrictions. Specifically, there are 3 places where generic type information is available (see http://www.cowtowncoder.com/blog/archives/2008/12/entry_126.html for details); on super-class/interface declaration (parameterization of super type), on field declarations, and and on method (argument, return type) declarations.

In case of TypeReference what happens is that you create an anonymous type with specified super type; and this information will then be available since that anonymous type (class) is passed.

StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • "really quite simple" - I would disagree with you there. – Simon Nickerson Jun 09 '11 at 17:01
  • Heh. Well, the idea that VALUE does not have generic type info, but Class definitions do is simple. Not generics as a whole, that I would not claim. :) But FWIW, this (http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html) can make it possible to understand how it all works. – StaxMan Jun 09 '11 at 17:05
  • @StaxMan, thank you for the answer and interesting links. – Yann-Gaël Guéhéneuc Apr 14 '13 at 13:11
0

I see no real contradiction here.

TypeReference will be considered as a raw TypeReference type at runtime for legacy code, but it still has the hability to provide information about SomeType. What type erasure means is that you can't use the T of TypeReference at runtime as examplified here. But you can still know by what T is replaced..

Snicolas
  • 37,840
  • 15
  • 114
  • 173