How to obtain the actual type parameters of the general interface indirectly implemented?
I have a parameterized interface that can be implemented in many different ways At runtime, I need to find out what the actual type parameters of the interface are given an arbitrary object that implements the interface
This is a fragment to illustrate the problem and a half-way attempt to solve the problem (also on idea. Com):
import java.util.*; import java.lang.reflect.*; interface Awesome<X> { } class Base<E> implements Awesome<Set<E>> { } class Child extends Base<List<Integer>> { } class AwesomeExample { public static void main(String[] args) { Awesome<Set<List<Integer>>> x = new Child(); System.out.println( ((ParameterizedType) Child.class.getGenericSuperclass() ).getActualTypeArguments()[0] ); // prints "java.util.List<java.lang.Integer>" System.out.println( ((ParameterizedType) Base.class.getGenericInterfaces()[0] ).getActualTypeArguments()[0] ); // prints "java.util.Set<E>" investigate(x); // we want this to print "Set<List<Integer>>" } static void investigate(Awesome<?> somethingAwesome) { // how to do this? } }
It seems that there is enough generic type information at runtime to infer:
>Child extension base < list < integer > > foundation < E > implementation of awesome < set < E > >
Therefore, we can put all the bits and pieces together and draw a conclusion:
>Child implements awesome < set < list < integer > >
So it seems that this problem can be solved, but it is not that simple, because we must use any class / interface hierarchy Is this the only way? Is there a simpler way? Has anyone written about the library?
Solution
Edit: you may just want to view using: http://code.google.com/p/gentyref/
If you can guarantee awesome All implementations of will not have type parameters. The following code should let you start [1]:
static void investigate(Object o) { final Class<?> c = o.getClass(); System.out.println("\n" + c.getName() + " implements: "); investigate(c,(Type[])null); } static void investigate(Type t,Type...typeArgs) { if(t == null) return; if(t instanceof Class<?>) { investigate((Class<?>)t,typeArgs); } else if(t instanceof ParameterizedType) { investigate((ParameterizedType)t,typeArgs); } } static void investigate(Class<?> c,Type...typeArgs) { investigate(c.getGenericSuperclass(),typeArgs); for(Type i : c.getGenericInterfaces()) { investigate(i,typeArgs); } } static void investigate(ParameterizedType p,Type...typeArgs) { final Class<?> c = (Class<?>)p.getRawType(); final StringBuilder b = new StringBuilder(c.getName()); b.append('<'); Type[] localArgs = p.getActualTypeArguments(); if(typeArgs != null && typeArgs.length > 0) { int i = 0,nextTypeArg = 0; for(Type local : localArgs) { if(local instanceof ParameterizedType) { ParameterizedType localP = (ParameterizedType) local; b.append(localP.getRawType()).append('<'); b.append(typeArgs[nextTypeArg++]); b.append('>'); } else if(local instanceof TypeVariable) { // reify local type arg to instantiated one. localArgs[nextTypeArg] = typeArgs[nextTypeArg]; b.append(localArgs[nextTypeArg]); nextTypeArg++; } else { b.append(local.toString()); } b.append(","); i++; } if(typeArgs.length > 0) { b.delete(b.length() - 2,b.length()); } b.append('>'); } else { String args = Arrays.toString(localArgs); b.append(args.substring(1,args.length()-1)).append('>'); } System.out.println(b); investigate(c,localArgs); }
But if awesome What about instantiation? Or after the base < E > will be deleted, the type information will be lost As a convention, this can be solved by:
Awesome<?> awesome = new Base<Double>() {};
Note {}, this will create a new anonymous class that implements (or extends here) base < E > The class will have type parameters that can be used for reflection
If you are afraid that enforcing this Convention will be a problem, you can hide the constructor & exposing only factory methods:
class Base<E> implements Awesome<Set<E>> { public static Base<Number> newNumberInstance() { return new Base<Number> () {}; } protected Base() {} }
Since the above code has not been fully tested, you may want to do so The point here is that given your strict requirements, you can find the actual type parameters It's up to you to decide whether it applies to your situation
[1] It prints out all the interfaces implemented by a class It's not just the type parameter of awesome This can be changed, but I think I'll choose a more general one & let you clarify the details For example, you need to test these to see what I mean:
investigate(new ArrayList<Integer>()); investigate(new ArrayList<String>() {}); // new anonymous ArrayList class investigate(""); investigate(new Awesome<Comparable<?>> () {}); // new anonymous implementation of Awesome