Given 2 interfaces:
public interface BaseInterface<T> { }
public interface ExtendedInterface<T0, T1> extends BaseInterface<T0> {}
and a concrete class:
public class MyClass implements ExtendedInterface<String, Object> { }
How do I find out the type parameter passed to the BaseInterface interface?
(I can retrieve the ExtendedInterface type parameters by calling something like
MyClass.class.getGenericInterfaces()[0].getActualTypeArguments()
but I can't spot an easy way to recurse into any base generic interfaces and get anything meaningful back).
When an interface is specified as a constraint on a type parameter, only types that implement the interface can be used. The following code example shows a SortedList<T> class that derives from the GenericList<T> class. For more information, see Introduction to Generics.
You can also use more than one type parameter in generics in Java, you just need to pass specify another type parameter in the angle brackets separated by comma.
In a generic type or method definition, a type parameter is a placeholder for a specific type that a client specifies when they create an instance of the generic type.
In order to use a generic type we must provide one type argument per type parameter that was declared for the generic type. The type argument list is a comma separated list that is delimited by angle brackets and follows the type name. The result is a so-called parameterized type.
I don't know what exactly you are trying to achieve, and what is known and what not, but you can recurse to the superinterface like this:
Type[] interfaces = MyClass.class.getGenericInterfaces();
ParameterizedType extInterfaceType = (ParameterizedType)interfaces[0];
Class<?> extInterfaceClass = (Class<?>)extInterfaceType.getRawType();
Type[] baseInterfaces = extInterfaceClass.getGenericInterfaces();
ParameterizedType baseInterfaceType = (ParameterizedType)baseInterfaces[0];
Class<?> baseInterfaceClass = (Class<?>)baseInterfaceType.getRawType();
Of course if you reach the second level that way you get only your names T0 and T1 as generic parameters. If you know the relationship between ExtendedInterface
and BaseInterface
you don't really have to go that far since you know which generic parameter of the former is passed to the latter. If not, you probably would have to loop through their parameters and find a match. Something based on this probably:
Type[] params = extInterfaceClass.getTypeParameters();
for (Type param : params) {
if (param == baseInterfaceType.getActualTypeArguments()[0]) {
// ...
}
}
This problem is not easy to fully solve in general. For example, you also have to take type parameters of the containing class into account if it's an inner class,...
Because reflection over generic types is so hard using just what Java itself provides, I wrote a library that does the hard work: gentyref. See http://code.google.com/p/gentyref/ For your example, using gentyref, you can do:
Type myType = MyClass.class;
// get the parameterized type, recursively resolving type parameters
Type baseType = GenericTypeReflector.getExactSuperType(myType, BaseInterface.class);
if (baseType instanceof Class<?>) {
// raw class, type parameters not known
// ...
} else {
ParameterizedType pBaseType = (ParameterizedType)baseType;
assert pBaseType.getRawType() == BaseInterface.class; // always true
Type typeParameterForBaseInterface = pBaseType.getActualTypeArguments()[0];
System.out.println(typeParameterForBaseInterface);
}
This is difficult to solve using Java Reflection API because one needs to resolve all encountered type variables. Guava since version 12 has TypeToken class which contains fully resolved type info.
For your example, you can do:
TypeToken<? extends T> token = TypeToken.of(MyClass.class);
ParameterizedType type =
(ParameterizedType) token.getSupertype(BaseInterface.class).getType();
Type[] parameters = type.getActualTypeArguments();
Still you need to remember that this only works for cases when MyClass is not generic itself. Otherwise the value of type parameters is not available at runtime due to type erasure.
I don't think there's an direct way of getting the generic type of the base interface.
One way would be to declare a method in the interface like this:
public interface BaseInterface<T> {
Class<T> getGenericClass();
}
Also, I don't know what kind of control you have over these classes. You can always assert that all implementers have the base interface explicitly declared like:
public class MyClass implements ExtendedInterface<String, Object>, BaseInterface<String>{ }
and
MyClass.class.getGenericInterfaces()[1].getActualTypeArguments()[0]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With