4

Given two classes like this:

class Example1<A,B> {

  public Map<A,B> someMap = new HashMap<A,B>();

}

class Example2 extends Example1<URL, URL> {


}

Is there any way using reflection that I can determine the component types of the Map for Example2?

I know I can do:

ParameterizedType t = (ParameterizedType) Example2.class.getFields()[0].getGenericType();

But from there, I can't seem to figure out that the two components are URLs.

Does anyone have any ideas?

Rob Moffat
  • 41
  • 1
  • 2
  • Just curious... What would `t` contain? – danyim Aug 10 '10 at 15:24
  • 2
    dont ever use java.net.URL as a key for HashMap!! it's evil because it potentially often calls hostsEqual() which performs network I/O (dns lookups) – mhaller Jan 04 '11 at 23:08

4 Answers4

7

Darron is not completely correct. The following will print out what you want:

ParameterizedType superClass = (ParameterizedType) Example2.class.getGenericSuperclass();
System.out.println(superClass.getActualTypeArguments()[0]);
System.out.println(superClass.getActualTypeArguments()[1]);

Prints out:

class java.net.URL
class java.net.URL
Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
  • To clarify on the "type erasure" point, all the generic type information is actually encoded in the Java .class file. Where it's **not** encoded is within the runtime execution of a method. That is why the information is accessible through reflection, but is not accessible through standard language constructs (i.e. new T[] or o instanceof T). – Kirk Woll Aug 10 '10 at 15:40
  • That gets you the type parameters used by Example2 for its *direct* superclass. But to solve the general problem this question poses, you need to find type parameters for an arbitrary superclass, plus then substitute type variables possibly deep inside the type of a field. getGenericSuperclass() is only a small first step to reach that goal. There are many (sometimes tricky) cases involved in building a full solution. I wouldn't recommend (re)implementing that yourself unless you're *really* into generics. – Wouter Coekaerts Jan 14 '11 at 22:33
1

Because Java generics are implemented via "erasure", this information is not available at runtime through reflection.

EDIT: it seems I missed some detail of the question. In this sort of specialized case the type information is available.

Darron
  • 21,309
  • 5
  • 49
  • 53
  • 1
    You'd be correct if the reflection began with calling `getClass` on an instance of `HashMap` itself. Then Kirk's code would print something like `K` and `V` (the names of the type parameters; pretty useless). But because the reflection here begins with a class that *inherits* `HashMap`, the types supplied for `K` and `V` are stored in the class metadata for `Example2`, and so Kirk's code has some information to read from. – Daniel Earwicker Aug 10 '10 at 15:41
  • Rob is asking about a class, not an object. The information is available at runtime. – Tom Hawtin - tackline Aug 10 '10 at 15:45
1

Your answer is not quite correct - you are returning the generic types of the Class, not of "someMap" field type.

Of course, in the example given the arguments to someMap were , which are the same as the arguments to the class, but if someMap were defined with , and the map types were not both URL, then there is a question about how to map the generic type's parameters to the field's.

A better example might have been:

class Example1<A,B> {

  public Map<B,A> someMap = new HashMap<B,A>();

}

class Example2 extends Example1<URL, String> {


}

In this case, to answer the question of: what are someMap's types in Example2? The answer should be:

java.lang.String java.net.URL

But, I still can't figure out how to get that.

Rob Moffat
  • 465
  • 5
  • 11
1

The Java reflection API does not have a method that directly gives what you want, but it does expose all the basic information needed to be able to calculate it with Field.getGenericType() and Class.getGenericSuperclass(). But solving this problem in general for any class hierarchy and field type is not trivial.

To do this calculating you can use the gentyref library. In your example, you can get the type of the someMap variable like this:

Type someMapType = GenericTypeReflector.getExactFieldType(
   Example2.class.getField("someMap"),
   Example2.class);

If enough information is available (and in Example2, it is), someMapType will be a ParameterizedType. Then you can get the type of the key and the value like this:

((ParameterizedType)someMapType).getActualTypeArguments();

That will be array containing URL.class twice. Note that it can contain other things than classes too (another ParameterizedType for example). So if you want to do something non-trivial with that type (for example see if it's a supertype of another one), the other methods in GenericTypeReflector might come in handy too...

[I'm the author of gentyref]

Wouter Coekaerts
  • 9,395
  • 3
  • 31
  • 35