tutorial tipo parametrico objetos manejo interfaces instanciar genericos genericas generica datos creacion con clases java generics reflection

java - parametrico - Recuperar parámetros de tipo de una instancia de una interfaz base genérica



manejo de datos genericos con java (9)

No creo que haya una forma directa de obtener el tipo genérico de la interfaz base.

Una forma sería declarar un método en la interfaz de esta manera:

public interface BaseInterface<T> { Class<T> getGenericClass(); }

Además, no sé qué tipo de control tienes sobre estas clases. Siempre puede afirmar que todos los implementadores tienen la interfaz base explícitamente declarada como:

public class MyClass implements ExtendedInterface<String, Object>, BaseInterface<String>{ }

y

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

Dadas 2 interfaces:

public interface BaseInterface<T> { } public interface ExtendedInterface<T0, T1> extends BaseInterface<T0> {}

y una clase concreta:

public class MyClass implements ExtendedInterface<String, Object> { }

¿Cómo averiguo el parámetro de tipo pasado a la interfaz de BaseInterface?

(Puedo recuperar los parámetros del tipo ExtendedInterface llamando a algo así como

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

pero no puedo encontrar una manera fácil de recurrir en cualquier interfaz genérica básica y recuperar algo significativo).


No creo que puedas, ya que estos son realmente específicos de la instancia y no específicos de la clase. Considera lo siguiente:

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

El hecho de que a es una lista genérica de cadenas es específico para la instancia ay no para la lista de clases. Por lo tanto, ninguno de los métodos del objeto List.class podría decirle que el tipo genérico sería de tipo String para a. Aunque MyClass en su ejemplo tiene valores establecidos para los tipos genricizados de la interfaz, no creo que esto esté disponible en la instancia del objeto Clase de interfaz.


No sé qué es exactamente lo que estás tratando de lograr, y qué se sabe y qué no, pero puedes volver a recurrir a la superinterfaz de esta manera:

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

Por supuesto, si alcanzas el segundo nivel de esa manera solo obtendrás tus nombres T0 y T1 como parámetros genéricos. Si conoce la relación entre ExtendedInterface y BaseInterface , no tiene que ir demasiado lejos, ya que sabe qué parámetro genérico de la anterior se le pasa a este último. Si no, probablemente tendrías que recorrer sus parámetros y encontrar una coincidencia. Algo basado en esto probablemente:

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


Pienso que la única opción que puedo pensar es inspeccionar un método genérico declarado por BaseInterface , y no anulado.


Esto es difícil de resolver usando Java Reflection API porque uno necesita resolver todas las variables de tipo encontradas. Guava desde la versión 12 tiene clase TypeToken que contiene información de tipo completamente resuelta.

Para su ejemplo, puede hacer:

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

Sin embargo, debe recordar que esto solo funciona en los casos en que MyClass no es genérico. De lo contrario, el valor de los parámetros de tipo no estará disponible en el tiempo de ejecución debido al borrado de tipo.


Esto hace lo que buscas, pero todavía no está bien. Por ejemplo, no maneja el caso donde Foo<T> implements Bar<Map<T>> . Lo que realmente necesita es una forma de preguntarle a la JVM "está bien, aquí hay una lista de tipos. ¿Qué tipo real obtengo si los aplico a este tipo genérico?"

Pero este código hace lo que está buscando.

import java.lang.reflect.GenericDeclaration; import java.lang.reflect.ParameterizedType; import java.util.*; interface BaseInterface<T> {} interface FirstArg<T1,T2> extends BaseInterface<T1>{} interface SecondArg<T1,T2> extends BaseInterface<T2>{} class First implements FirstArg<Number, String> {} class Second implements SecondArg<Number, String> {} 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<t2.getActualTypeArguments().length; i++) { System.out.println("Parameter " + c2.getTypeParameters()[i] + " is " + t2.getActualTypeArguments()[i]); } ParameterizedType t3 = (ParameterizedType) c2.getGenericInterfaces()[0]; System.out.println(t2 + " implements " + t3); System.out.println("and so that means we are talking about/n" + t3.getRawType().toString() + " <"); for(int i = 0 ; i< t3.getActualTypeArguments().length; i++) { System.out.println("/t" + t3.getActualTypeArguments()[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(); } }


Este problema no es fácil de resolver completamente en general. Por ejemplo, también debe tener en cuenta los parámetros de tipo de la clase contenedora si se trata de una clase interna, ...

Debido a que la reflexión sobre los tipos genéricos es tan difícil usando solo lo que Java proporciona, escribí una biblioteca que hace el trabajo arduo: gentyref. Ver http://code.google.com/p/gentyref/ Para su ejemplo, usando gentyref, puede hacer:

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


mala etiqueta otra vez de mi contestando mi propia pregunta.

Como señaló gix, en el momento en que comienzas a recorrer una jerarquía de tipos genéricos, más allá del primero, pierdes información sobre los argumentos de tipo.

Pero los bits importantes son: Obtiene los argumentos de tipo de la primera interfaz genérica que se instanciará (en mi ejemplo, ExtendedInterface), y también obtendrá los nombres de los parámetros de tipo utilizados para crear las subinterfaces.

Por lo tanto, es posible determinar los argumentos de tipo para las interfaces de base manteniendo un mapa de nombres de tipoVariable a los argumentos de tipo reales.

Actualizaré algún código más tarde, pero funciona (puede determinar el parámetro de tipo utilizado para la instancia BaseInterface, de MyClass.class).

Actualización Este es un primer pase que ilumina en verde algunas pruebas simples de unidades. Necesita trabajo ... La verdadera pregunta es, ¿el problema merece una solución tan absurda?

public class GenericReflectionUtils { @SuppressWarnings("unchecked") public static List<Class> 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<Class> 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<Class> 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<Class> typeArgs = new ArrayList<Class>(actualTypeArguments.length); for (Type typeArg : actualTypeArguments) { typeArgs.add(getClass(typeArg)); } return typeArgs; } // currentType is derived Map<String, Class> typeVarMap = createTypeParameterMap(currentType, null); for (Type genericInterfaceType : currentClass.getGenericInterfaces()) { List<Class> result = getGenericInterfaceTypeArguments(baseInterface, (ParameterizedType)genericInterfaceType, typeVarMap); if (result != null) { return result; } } return null; } private static Map<String, Class> createTypeParameterMap(ParameterizedType type, Map<String, Class> extendedTypeMap) { Map<String, Class> typeVarMap = new HashMap<String, Class>(); 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<Class> createTypeParameterList(Map<String, Class> typeParameterMap, ParameterizedType type) { ArrayList<Class> typeParameters = new ArrayList<Class>(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<Class> getGenericInterfaceTypeArguments(Class baseInterface, ParameterizedType currentType, Map<String, Class> 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<Class> result = getGenericInterfaceTypeArguments(baseInterface, (ParameterizedType)genericInterface, currentTypeParameters); if (result != null) { return result; } } return null; }

}