• Home
  • RSS Feed
  • Log in


A general-purpose utility to retrieve Java generic type values
Posted by Andrew Phillips in the early afternoon: March 12th, 2009

In a recent post, Arjan Blokzijl discussed how Class.getGenericSuperclass can be used to access generic type information.

This can be very useful, but implementing a general-purpose method to do this isn’t as straightforward as it might seem. In this post, we’ll outline some of the issues and hopefully resolve them, creating a utility class in the process. After all, with luck this is the kind of code that only needs to be written once! For the curious or impatient, here it is.

Before diving into the details, it should be noted that this utility can only access generic type information available in the class hierarchy at compile time; for example, the Agent in

class SecretAgentVehicle extends Vehicle

Java’s implementation of generics via type erasure means that it is not possible to access runtime type information – there is no way to get the Agent from

List list = new ArrayList();

as it is specified neither in the interface List nor ArrayList’s superclass AbstractList. I’d love to be wrong about this, by the way – if you have some clever way of accessing this information, please let me know!

Now, as Arjan demonstrated, generic type information is available from ParameterizedType.getActualTypeArguments, where the ParameterizedType is returned by Class.getGenericSuperclass or Class.getGenericInterfaces (you’ll need a cast from Type).

However, this only works if the parent is a generic class (respectively, if the class implements a generic interface). If the parent is a “normal” class, the Type returned will simply be the result of Class.getSuperclass (respectively, Class.getInterfaces) and you’ll get a ClassCastException trying to cast to ParameterizedType. Ouch!

So what to do in a situation such as

class DoubleOhVehicle extends SecretAgentVehicle

in which we would presumably still like to know that DoubleOhVehicle has type parameter Agent? Well, you simply have to climb up the class hierarchy until you reach the desired class, and a loop would be one way to do this (a recursive implementation is also a good fit). Note that we assume that the input class is, indeed, a SecretAgentVehicle subclass, something that can be enforced using bounded type parameters, for instance.

assert SecretAgentVehicle.class.isAssignableFrom(clazz);

Type supertype = clazz;

do {
  supertype = supertype.getGenericSuperclass();
} while (!supertype.equals(Vehicle.class);

Type[] actualTypeArguments = ((ParameterizedType) supertype).getActualTypeArguments();

Or rather, it would be nice if you could write something like that…but it won’t work. Unfortunately, Type doesn’t have a getGenericSuperclass method – for the hiearchy traversal Class instances must be explicitly used.

Class clazz;

while (!clazz.equals(SecretAgentVehicle.class)) {
  clazz = clazz.getSuperclass();
}

Type[] actualTypeArguments =
  ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments();

Voila! We can now cope with subclasses. What else could there be to do?

Well, consider the following class hierarchy:

class Activity

class SecretAgentActivity extends Activity

class SecretAgentMission extends SecretAgentActivity

class BondMission extends SecretAgentMission

Clearly, the (ordered!) type parameters of BondMission are Agent and Mission, but

  ((ParameterizedType) SecretAgentActivity.class.getGenericSuperclass()).getActualTypeArguments();

only returns [Agent, S]. Annoying as this may be, it is, indeed, all that can be expected: SecretAgentActivity.class has no way of knowing that the second parameter is Mission, since that is only defined in a subclass. Note also that the “uninstatiated” variable is returned as S rather than V, i.e. using the type parameter from the subclass.

In order to deal with this situation, therefore, we need not only to traverse up the hierarchy (bearing in mind that not all classes in the hierarchy are necessarily generic), but also keep track of type parameter/instance mappings as we go.
How does this work? Well, in the example, we can compare the type parameters for SecretAgentActivity.class with the actual type arguments to discover that parameter S is mapped to type Mission. Then, when examining the actual parameters [Agent, S] of Activity.class, we can use this mapping to “resolve” S to Mission, obtaining the desired result.

Since our aim is to write a general-purpose method, there is a further question we haven’t addressed. Traversing the class hierarchy up from a given class is fine, but…when to stop? In the above example, the question “What is the type parameter for BondMission as a SecretAgentActivity (answer: [Mission])?” may be just as valid as “What are the parameters for BondMission as an Activity ([Agent, Mission])?”
For this reason, the method accepts two arguments: the class whose type arguments are required and the “context” class it is to be regarded a subclass of.

What if the target class and the “context” superclass are the same, or what if someone asks for the types of SecretAgentActivity.class as a subclass of Activity? Well, we know that not all the type variables will be available, but how should the code respond?

In this case, null will be returned for “unresolvable” type parameters (so SecretAgentActivity would have types [Agent, null] as an Activity subclass). This just seems like a reasonable response (besides being easy to implement), but could easily be changed, if desired.



Our discussion up until this point has focused only on class hierarchies. But with so many generic interfaces, our utility would be much more useful if the subclass or the context superclass, or both, could be interfaces. For instance, for a set of classes

interface AgentAttributes extends Map

interface AgentCodenames extends AgentAttributes

class DigitCodenames implements AgentCodenames

we would also like to be able to answer

  • getActualTypeArguments(DigitCodenames.class, AgentAttributes.class) with [String]
  • and

  • getActualTypeArguments(AgentCodenames.class, Map.class) with [Agent, String]
  • .

In principle, this should only require minor changes. One such is that interface type information is available via Class.getGenericInterfaces, not Class.getGenericSuperclass, and we’ll need to extract the interface under consideration from the array of interfaces (not all of which are necessarily generic!) returned.

There is a more fundamental difference, though: the inheritance path from a class or interface to a superinterface is not necessarily unique! This is perfectly acceptable

interface Person

interface Assassin extends Person
interface Ladykiller extends Person

interface Agent extends Assassin, Ladykiller

where both [Agent, Assassin, Person] and [Agent, Ladykiller, Person] are paths from Agent to Person.

Which to choose? Well, thankfully, it doesn’t matter in this case, because the compiler forbids conflicting (typed) inheritance paths, such as

interface Registry extends Set

interface CodenameRegistry extends Registry
interface DoubleOhRegistry extends Registry

interface Mi6Registry extends CodenameRegistry, DoubleOhRegistry

As a result, we can simply choose one of the paths, using ClassUtils.getSuperclassChain.

That link to the code once again.

Share

Tags: generics
Filed under Java | 9 Comments »



9 Responses to “A general-purpose utility to retrieve Java generic type values”



    florin Says:
    Posted at: March 13, 2009 at 6:30 pm

    Looks great.

    Where is the SetUtils.java that is imported?

    Reply


    Andrew Phillips Says:
    Posted at: March 13, 2009 at 7:41 pm

    @florin: Eek, yes, thanks for that. I’ve added a link to SetUtils.java on the source page- it’s nothing magical, simply a convenience method like Arrays.asList for Sets…

    Reply


    Dan Howard Says:
    Posted at: March 13, 2009 at 10:04 pm

    Tres cool.

    Reply


    erikjan Says:
    Posted at: March 22, 2009 at 5:09 pm

    I think there is a way to get the type of List list;

    Execute this code and see that it will print Intger in the end.

    public class FieldSpy {
      public List<Integer> list;
    
      public static void main(String... args) {
        try {
          Class c = Class.forName("FieldSpy");
          Field f = c.getField("list");
          System.out.format("Type: %s%n", f.getType());
          System.out.format("GenericType: %s%n", f.getGenericType());
    
          Type type = f.getGenericType();
          if (type instanceof ParameterizedType) {
              System.out.println(((ParameterizedType)type).getActualTypeArguments()[0]);
          }
        } catch (ClassNotFoundException x) {
          x.printStackTrace();
        } catch (NoSuchFieldException x) {
          x.printStackTrace();
        }
      }
    }
    
    Reply


    Andrew Phillips Says:
    Posted at: March 23, 2009 at 12:06 pm

    @erikjan: I assume you meant

    List<Integer> list;
    

    and adjusted your the comment accordingly – I think the angle brackets were stripped out as “invalid HTML” ;-) .

    Indeed, running the code prints

    Type: interface java.util.List
    GenericType: java.util.List
    class java.lang.Integer

    as you mention. This also works using Class c = Foo.class.

    Thanks for that, very useful. But it does require you to have access to the field declaration of list, i.e. compile-time information.

    What I meant – and could have expressed more clearly – when I wrote “there is no way to get the Agent from” is that you cannot access the type information if given only the list object.

    Reply


    Erik Jan Says:
    Posted at: March 29, 2009 at 2:33 pm

    Ok that’s right, I guess that is the only way they could have implemented generics, as Sun always wants to be backwards compatibility. They face the same problems with closures. If only they put in right from the beginning

    Reply


    Neal Gafter Says:
    Posted at: April 2, 2009 at 5:04 pm

    Erik: I’m not aware of any compatibility issues that are obstacles to adding closures to Java. Are you?

    Reply


    Andrew Phillips Says:
    Posted at: June 21, 2009 at 9:02 pm

    PS: The code for this project is now available at Google Code (in the commons-lang project).

    Reply


    Myšlenky dne otce Fura » Blog Archive » Oříšek v reflexní analýze generik Says:
    Posted at: March 19, 2010 at 8:14 am

    [...] Velmi pěkně popsané pozadí analýzy generik pomocí reflexe [...]

    Reply


Leave a Reply

Click here to cancel reply.


Xebia Sites

  • Xebia Corporate
  • Xebia France
  • Xebia India
  • Xebia Sweden

Categories

  • Java (311)
  • Agile (181)
  • General (136)
  • Scrum (67)
  • Architecture (64)
  • Testing (59)
  • Performance (46)
  • Middleware (56)
    • Deployment (38)
  • Xebia Labs (39)
  • SOA (31)
  • Podcast (31)
  • Project Management (28)
  • Tools (26)
  • Uncategorized (20)
  • lean architecture (20)
  • Quality Assurance (17)
  • Articles (13)
  • Requirements Management (13)
  • Virtualization (19)

Tag Cloud

    Groovy Scrum Architecture JPA implementation patterns ACT XML JPA Agile TDD agile architectuur Frameworks lean architecture Oracle Ajax Java lean architectuur Moving to India Javascript Scala Concurrency Control Eclipse SOA Lean Flex product owner Hibernate Grails Spring Maven Xebia

Archives

  • February 2012
  • January 2012
  • December 2011
  • November 2011
  • October 2011
  • September 2011
  • August 2011
  • July 2011
  • June 2011
  • May 2011
  • April 2011
  • March 2011
Avatars by Sterling Adventures