views:

205

answers:

3

Most of the documentation regarding type erasure handling in Java assumes that the use case is handling a type like SomeType<ParamType>. I am trying to process method parameter for the following method:

public void setOtherReferenceRanges(List<ReferenceRange<T>> referenceRanges)

When the container class is instantiated with a type DvQuantity, this signature should become public void setOtherReferenceRanges(List<ReferenceRange<DvQuanitity>> referenceRanges) in runtime.

Using reflection one can see that the List has an actualTypeArgument which is ReferenceRange<T>. Since reflection uses class information, I would not expect it to give me ReferenceRange<DvQuantity>. However, when I created the class containing this method, I passed the DvQuantity type as T. So the type filling in T should be available to Java runtime, but I could not find a way of getting it. I end up with a TypeVariableImpl object accessed via reflection, which does not seem to contain any useful data.

Can you think of any ways to discover this information in runtime?

A: 

Generic type information is erased by the compiler and is not available at runtime. When I need to ensure a certain type at runtime I pass in a class argument:

public <T> void doSomething(T t, Class<T> c);

This is not always convenient or even possible, but for many cases it is possible.

Mr. Shiny and New
+1  A: 

When you say

when I created the class containing this method

I guess you mean when you create an object of that type, for example:

foo = new ContainerClass<DvQuantity>();

In that case, because of erasure, there is no way to recover that DvQuantity.

However, if you create a class passing a type parameter to the superclass, like this class DvQuantityContainerClass extends ContainerClass {...} ... foo = new DvQuantityContainerClass();

Or, shorter, an inline anonymous subclass (which looks almost like the first example but with a subtle but important difference):

foo = new ContainerClass<DvQuantity>(){};

Then you can recover the type parameter, because you recover the type parameter used to extend a superclass at runtime. Unfortunately, Java itself doesn't provide an easy way to now get the type of the DvQuantityContainerClass.setOtherReferenceRanges method with the T filled in. For that, I've written gentyref, to do advanced reflection on generic types:

Method m = DvQuantityContainerClass.class.getMethod("setOtherReferenceRanges", List.class);
// this will return List<ReferenceRange<DvQuanity>>, like you are lookingn for
return GenericTypeReflector.getExactParameterTypes(m, DvQuantityContainerClass.class)
Wouter Coekaerts
Thanks for the response. It validates what I've seen so far. Unfortunately I have no power over the setup where I need to make this work. Also thanks to other responses, all of which would be useful if I had control over the situation.
A: 

So the type filling in T should be available to Java runtime, but I could not find a way of getting it.

Perhaps it's not entirely correct, but the way I think about it is that at runtime there is no actual class - just an object without a specific type which meets the interface of T. In other words, erasure happens not with objects, but instead with these nebulous (in the OOP world at least) type-things.

http://java.sun.com/docs/books/tutorial/java/generics/erasure.html

There are ways of capturing the type information inside the class itself (T types would need a method getUnderlyingType()... or something), but that's a bad idea. If you truly need to raw type of the object, I'd reconsider using generics.

Quotidian