Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrieving type parameters from an instance of a generic base interface

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).

like image 243
Andy Avatar asked Feb 17 '09 16:02

Andy


People also ask

Can type parameters be used with interfaces?

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.

How many type parameters can be used in a generic class?

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.

What is type parameters in generics?

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.

How do you provide a generic parameterized 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.


4 Answers

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]) {
        // ...
    }
}
like image 39
gix Avatar answered Sep 21 '22 06:09

gix


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);
}
like image 97
Wouter Coekaerts Avatar answered Sep 19 '22 06:09

Wouter Coekaerts


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.

like image 25
Sławek Avatar answered Sep 22 '22 06:09

Sławek


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]
like image 26
bruno conde Avatar answered Sep 20 '22 06:09

bruno conde