从通用基接口的实例中检索类型参数

给出2个接口:

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

和一个具体的类:

 public class MyClass implements ExtendedInterface { } 

如何找到传递给BaseInterface接口的type参数?

(我可以通过调用类似的东西来检索ExtendedInterface类型参数

 MyClass.class.getGenericInterfaces()[0].getActualTypeArguments() 

但我无法找到一种简单的方法来递归到任何基础通用接口并得到任何有意义的东西)。

这个问题一般不容易完全解决。 例如,如果它是一个内部类,你还必须考虑包含类的类型参数,…

因为使用Java本身提供的对generics类型的反思是如此困难,所以我编写了一个完成艰苦工作的库:gentyref。 请参阅http://code.google.com/p/gentyref/对于您的示例,使用gentyref,您可以执行以下操作:

 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); } 

我不知道你究竟想要实现什么,知道什么和不知道什么,但你可以像这样递归到超级接口:

 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(); 

当然,如果以这种方式达到第二级,则只能将名称T0和T1作为通用参数。 如果您知道ExtendedInterfaceBaseInterface之间的关系, BaseInterface您不必走得那么远,因为您知道前者的哪个generics参数传递给后者。 如果没有,您可能需要遍历其参数并找到匹配项。 可能基于此的东西:

 Type[] params = extInterfaceClass.getTypeParameters(); for (Type param : params) { if (param == baseInterfaceType.getActualTypeArguments()[0]) { // ... } } 

使用Java Reflection API很难解决这个问题,因为需要解析所有遇到的类型变量。 从版本12开始,Guava具有TypeToken类,其中包含完全解析的类型信息。

举个例子,你可以这样做:

 TypeToken token = TypeToken.of(MyClass.class); ParameterizedType type = (ParameterizedType) token.getSupertype(BaseInterface.class).getType(); Type[] parameters = type.getActualTypeArguments(); 

仍然需要记住,这仅适用于MyClass本身不是通用的情况。 否则,由于类型擦除,类型参数的值在运行时不可用。

我认为没有直接的方法来获得基本接口的generics类型。

一种方法是在接口中声明一个方法,如下所示:

 public interface BaseInterface { Class getGenericClass(); } 

另外,我不知道你对这些课程有什么样的控制。 您始终可以断言所有实现者都明确声明了基本接口,如:

 public class MyClass implements ExtendedInterface, BaseInterface{ } 

 MyClass.class.getGenericInterfaces()[1].getActualTypeArguments()[0] 

有点像你追求的那样,但它仍然是不对的。 例如,它不处理Foo implements Bar> 。 你真正需要的是一些问jvm的方法“好的,这是一个类型列表。如果我将这些应用于这种generics类型,我会得到什么样的实际类型?”

但是,这个代码有点像你追求的那样。

 import java.lang.reflect.GenericDeclaration; import java.lang.reflect.ParameterizedType; import java.util.*; interface BaseInterface {} interface FirstArg extends BaseInterface{} interface SecondArg extends BaseInterface{} class First implements FirstArg {} class Second implements SecondArg {} public class Example { public static void main(String[] av) { new Example().go(); } void go() { test(First.class); test(Second.class); } void test(Class c1) { ParameterizedType t2 = (ParameterizedType) c1.getGenericInterfaces()[0]; System.out.println(c1 + " implements " + t2 ); Class c2 = (Class)t2.getRawType(); GenericDeclaration g2 = (GenericDeclaration) c2; System.out.println(t2 + " params are " + Arrays.asList(g2.getTypeParameters())); System.out.println("So that means"); for(int i = 0; i " + Arrays.asList(g2.getTypeParameters()).indexOf(t3.getActualTypeArguments()[i]) + " -> " + t2.getActualTypeArguments()[Arrays.asList(g2.getTypeParameters()).indexOf(t3.getActualTypeArguments()[i])] ); } System.out.println(">"); System.out.println(); } } 

我不认为你可以,因为这些实际上是特定于实例而不是特定于类。 考虑以下:

 List a = new ArrayList(); 

a是通用的字符串列表这一事实特定于实例a而不是类List。 因此,List.class对象的所有方法都不能告诉您generics类型的类型为String。 虽然您的示例中的MyClass碰巧为接口的genricized类型设置了值,但我认为这不会在接口Class对象实例中可用。

我认为我能想到的唯一选择是检查由BaseInterface声明的generics方法,而不是重写。

我回答自己的问题再次表达了礼貌。

正如gix指出的那样,当你开始走向generics类型的层次结构时,除了第一个之外,你就会丢失有关类型参数的信息。

但重要的部分是:您获得要实例化的第一个通用接口的类型参数(在我的示例中为ExtendedInterface),并且还获取用于创建子接口的类型参数的名称。

因此,可以通过将TypeVariable名称的映射保持为实际类型参数来确定基接口的类型参数。

稍后我将使用一些代码进行更新,但它确实有效(您可以确定用于实例BaseInterface的类型参数,来自MyClass.class)。

更新这是第一次绿灯点亮一些简单的unit testing。 它需要工作……真正的问题是,问题是否值得这样一个荒唐的解决方案?

 public class GenericReflectionUtils { @SuppressWarnings("unchecked") public static List getGenericInterfaceTypeArguments(Class baseInterface, Class concreteClass) { if (!baseInterface.isAssignableFrom(concreteClass)) { throw new IllegalArgumentException("Illegal base interface argument"); } if (concreteClass.getTypeParameters().length > 0) { throw new IllegalArgumentException("Can't determine the type arguments of a generic interface of a generic class"); } for (Type genericInterface : concreteClass.getGenericInterfaces()) { List result = null; if (genericInterface instanceof Class) { result = getGenericInterfaceTypeArguments(baseInterface,(Class)genericInterface); } else { result = getGenericInterfaceTypeArguments(baseInterface, (ParameterizedType)genericInterface); } if (result != null) { return result; } } return null; } public static Class getClass(Type type) { if (type instanceof Class) { return (Class) type; } if (type instanceof ParameterizedType) { return getClass(((ParameterizedType) type).getRawType()); } if (type instanceof GenericArrayType) { Type componentType = ((GenericArrayType) type).getGenericComponentType(); Class componentClass = getClass(componentType); if (componentClass != null) { return Array.newInstance(componentClass, 0).getClass(); } return null; } return null; } @SuppressWarnings("unchecked") private static List getGenericInterfaceTypeArguments(Class baseInterface, ParameterizedType currentType) { Class currentClass = getClass(currentType); if (!baseInterface.isAssignableFrom(currentClass)) { // Early out - current type is not an interface that extends baseInterface return null; } Type[] actualTypeArguments = currentType.getActualTypeArguments(); if (currentClass == baseInterface) { // currentType is a type instance of the base generic interface. Read out the type arguments and return ArrayList typeArgs = new ArrayList(actualTypeArguments.length); for (Type typeArg : actualTypeArguments) { typeArgs.add(getClass(typeArg)); } return typeArgs; } // currentType is derived Map typeVarMap = createTypeParameterMap(currentType, null); for (Type genericInterfaceType : currentClass.getGenericInterfaces()) { List result = getGenericInterfaceTypeArguments(baseInterface, (ParameterizedType)genericInterfaceType, typeVarMap); if (result != null) { return result; } } return null; } private static Map createTypeParameterMap(ParameterizedType type, Map extendedTypeMap) { Map typeVarMap = new HashMap(); Type[] typeArgs = type.getActualTypeArguments(); TypeVariable[] typeVars = getClass(type).getTypeParameters(); for (int typeArgIndex = 0; typeArgIndex < typeArgs.length; ++typeArgIndex) { // Does not deal with nested generic arguments... Type typeArg = typeArgs[typeArgIndex]; if (typeArg instanceof TypeVariable) { assert extendedTypeMap != null; TypeVariable typeVar = (TypeVariable)typeArg; typeVarMap.put(typeVars[typeArgIndex].getName(), extendedTypeMap.get(typeVar.getName())); continue; } typeVarMap.put(typeVars[typeArgIndex].getName(), getClass(typeArgs[typeArgIndex])); } return typeVarMap; } private static List createTypeParameterList(Map typeParameterMap, ParameterizedType type) { ArrayList typeParameters = new ArrayList(typeParameterMap.size()); for (Type actualType : type.getActualTypeArguments()) { if (actualType instanceof TypeVariable) { // Handles the case when an interface is created with a specific type, rather than a parameter typeParameters.add(typeParameterMap.get(((TypeVariable)actualType).getName())); continue; } typeParameters.add(getClass(actualType)); } return typeParameters; } @SuppressWarnings("unchecked") private static List getGenericInterfaceTypeArguments(Class baseInterface, ParameterizedType currentType, Map currentTypeParameters) { Class currentClass = getClass(currentType); if (!baseInterface.isAssignableFrom(currentClass)) { // Early out - current type is not an interface that extends baseInterface return null; } if (currentClass == baseInterface) { return createTypeParameterList(currentTypeParameters, currentType); } currentTypeParameters = createTypeParameterMap(currentType, currentTypeParameters); for (Type genericInterface : currentClass.getGenericInterfaces()) { List result = getGenericInterfaceTypeArguments(baseInterface, (ParameterizedType)genericInterface, currentTypeParameters); if (result != null) { return result; } } return null; } 

}

Apache Commons有一个实用程序可以做到这一点…… http://commons.apache.org/lang/api/org/apache/commons/lang3/reflect/TypeUtils.html

看看它如何在他们的github页面上使用: https : //github.com/apache/commons-lang/blob/72be39f4facb4a5758b9f646309328b764216da3/src/test/java/org/apache/commons/lang3/reflect/TypeUtilsTest.java