Retrieving type parameters from an instance of a generic base interface

前端 未结 9 746
悲&欢浪女
悲&欢浪女 2020-12-14 18:27

Given 2 interfaces:

public interface BaseInterface { }
public interface ExtendedInterface extends BaseInterface {}


        
相关标签:
9条回答
  • 2020-12-14 18:30

    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.

    0 讨论(0)
  • 2020-12-14 18:36

    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]) {
            // ...
        }
    }
    
    0 讨论(0)
  • 2020-12-14 18:43

    I think about the only option I can think of is to inspect a generic method which is declared by BaseInterface, and not overridden.

    0 讨论(0)
  • 2020-12-14 18:44

    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);
    }
    
    0 讨论(0)
  • 2020-12-14 18:45

    I don't think you can as these are really instance specific not class specific. Consider the following:

    List<String>  a = new ArrayList<String>();
    

    The fact that a is generic list of Strings is specific to the instance a and not to the class List. Thus, none of the methods of the List.class object could tell you that the genericized type would be of type String for a. Although MyClass in your example happens to have set values for the genricized types of the interface, i do not think this would be available at the interface Class object instance.

    0 讨论(0)
  • 2020-12-14 18:46

    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]
    
    0 讨论(0)
提交回复
热议问题