tipo para hacer google codigo buscador java generics reflection interface

java - hacer - ¿Cómo obtener los argumentos de tipo reales para una interfaz genérica implementada indirectamente?



buscador tipo google en java web (3)

Editar: es posible que desee examinar el uso de: http://code.google.com/p/gentyref/

Si puede garantizar que todas las implementaciones de Awesome<?> No tendrán argumentos de tipo, el siguiente código debería ayudarlo a comenzar [1]:

static void investigate(Object o) { final Class<?> c = o.getClass(); System.out.println("/n" + c.getName() + " implements: "); investigate(c, (Type[])null); } static void investigate(Type t, Type...typeArgs) { if(t == null) return; if(t instanceof Class<?>) { investigate((Class<?>)t, typeArgs); } else if(t instanceof ParameterizedType) { investigate((ParameterizedType)t, typeArgs); } } static void investigate(Class<?> c, Type...typeArgs) { investigate(c.getGenericSuperclass(), typeArgs); for(Type i : c.getGenericInterfaces()) { investigate(i, typeArgs); } } static void investigate(ParameterizedType p, Type...typeArgs) { final Class<?> c = (Class<?>)p.getRawType(); final StringBuilder b = new StringBuilder(c.getName()); b.append(''<''); Type[] localArgs = p.getActualTypeArguments(); if(typeArgs != null && typeArgs.length > 0) { int i = 0, nextTypeArg = 0; for(Type local : localArgs) { if(local instanceof ParameterizedType) { ParameterizedType localP = (ParameterizedType) local; b.append(localP.getRawType()).append(''<''); b.append(typeArgs[nextTypeArg++]); b.append(''>''); } else if(local instanceof TypeVariable) { // reify local type arg to instantiated one. localArgs[nextTypeArg] = typeArgs[nextTypeArg]; b.append(localArgs[nextTypeArg]); nextTypeArg++; } else { b.append(local.toString()); } b.append(", "); i++; } if(typeArgs.length > 0) { b.delete(b.length() - 2, b.length()); } b.append(''>''); } else { String args = Arrays.toString(localArgs); b.append(args.substring(1, args.length()-1)).append(''>''); } System.out.println(b); investigate(c, localArgs); }

Sin embargo, si se crean instancias de Awesome<?> O Base<E> , ese tipo de información se perderá debido al borrado. Esto se puede solucionar, como una convención, con algo como esto:

Awesome<?> awesome = new Base<Double>() {};

Observe el {} , esto crea una nueva clase anónima que implementa (o extiende) Base<E> . Esta clase tendrá sus parámetros de tipo disponibles para la reflexión.

Si teme que la aplicación de esta convención sea un problema, puede ocultar los constructores y solo exponer los métodos de fábrica:

class Base<E> implements Awesome<Set<E>> { public static Base<Number> newNumberInstance() { return new Base<Number> () {}; } protected Base() {} }

Como el código anterior no se ha probado por completo, es posible que desee hacerlo. El punto aquí es que puede encontrar los parámetros de tipo reales dado que sus requisitos son lo suficientemente estrictos. Si eso se aplica o no a su situación depende de usted para determinar.

[1] Imprimirá todas las interfaces que implementa una clase y no solo los parámetros de tipo de Awesome . Esto se puede cambiar, pero pensé que iría por más general y te dejaré resolver los detalles. Por ejemplo, querrás probarlos para ver lo que quiero decir:

investigate(new ArrayList<Integer>()); investigate(new ArrayList<String>() {}); // new anonymous ArrayList class investigate(""); investigate(new Awesome<Comparable<?>> () {}); // new anonymous implementation of Awesome

Tengo una interfaz parametrizada que se implementa de muchas maneras diferentes. En tiempo de ejecución necesito descubrir, dado un objeto arbitrario que implementa esa interfaz, cuáles son los parámetros de tipo reales para la interfaz.

Aquí hay un fragmento para ilustrar el problema, y ​​un intento a medias para resolverlo ( también en ideone.com ):

import java.util.*; import java.lang.reflect.*; interface Awesome<X> { } class Base<E> implements Awesome<Set<E>> { } class Child extends Base<List<Integer>> { } class AwesomeExample { public static void main(String[] args) { Awesome<Set<List<Integer>>> x = new Child(); System.out.println( ((ParameterizedType) Child.class.getGenericSuperclass() ).getActualTypeArguments()[0] ); // prints "java.util.List<java.lang.Integer>" System.out.println( ((ParameterizedType) Base.class.getGenericInterfaces()[0] ).getActualTypeArguments()[0] ); // prints "java.util.Set<E>" investigate(x); // we want this to print "Set<List<Integer>>" } static void investigate(Awesome<?> somethingAwesome) { // how to do this? } }

Parece que hay suficiente información de tipo genérico en tiempo de ejecución para deducir que:

  • Child extends Base<List<Integer>>
  • Base<E> implements Awesome<Set<E>>

Y, por lo tanto, podemos unir todas las partes para concluir que:

  • Child implements Awesome<Set<List<Integer>>>

Parece que el problema es solvente, pero no es tan simple, ya que tendríamos que trabajar con una jerarquía de clase / interfaz arbitraria. ¿Es esta la única manera de hacer esto? ¿Hay alguna forma más simple? ¿Alguien ha escrito una biblioteca para hacer esto ya?


La respuesta corta es no. Estoy de acuerdo en que es una pena ... :( La razón es que Java elimina los parámetros de tipo en la etapa de compilación. No existen en el código de bytes. Son utilizados solo por el compilador.

Para resolver su problema, debe agregar otro parámetro "regular" de tipo Clase y pasarlo al constructor cuando está creando una instancia de Base:

class Base<E> implements Awesome<Set<E>> { private E type; public Base(E type) { this.type = type; } }


Siguiendo la propuesta de @ oconnor0, aquí está cómo hacerlo con gentyref :

static Type investigate(Awesome<?> somethingAwesome) { return GenericTypeReflector.getTypeParameter(somethingAwesome.getClass(), Awesome.class.getTypeParameters()[0]); }

En caso de que somethingAwesome.getClass() también sea genérico, podría ser útil pasarlo primero a través de GenericTypeReflector.addWildcardParameters .

Spring también tiene un GenericTypeResolver.resolveTypeArgument (Class, Class) que puede lograr el mismo resultado.