I have a parameterized interface that is implemented in many different ways. At run time I need to figure out, given an arbitrary object that implements that interface, what the
Edit: You may just want to look into using: http://code.google.com/p/gentyref/
If you can guarantee that all implementations of Awesome>
will not have type arguments, the following code should get you started [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);
}
If, however, instantiations of Awesome>
or Base
will be made, that type information will be lost due to erasure. This can be worked around, as a convention, with something like this:
Awesome> awesome = new Base() {};
Notice the {}
, this creates a new anonymous class that implements (or here extends) Base
. This class will have its type parameters available for reflection.
If you're afraid enforcing this convention will be an issue, you can hide the constructors & only expose factory methods:
class Base implements Awesome> {
public static Base newNumberInstance() {
return new Base () {};
}
protected Base() {}
}
As the above code has not been completely tested, you may want to do that. The point here is that you can find the actual type parameters given your requirements are stringent enough. Whether or not that applies to your situation is up to you to determine.
[1] It will print out all of the interfaces a class implements & not just the type parameters of Awesome
. This can be changed, but I figured I'd go for more general & let you work out the specifics. For example, you'll want to test these to see what I mean:
investigate(new ArrayList());
investigate(new ArrayList() {}); // new anonymous ArrayList class
investigate("");
investigate(new Awesome> () {}); // new anonymous implementation of Awesome