Para poder inspeccionar programáticamente un objeto List y ver su tipo genérico. Un método puede querer insertar objetos basados en el tipo genérico de la colección. Esto es posible en idiomas que implementan genéricos en tiempo de ejecución en lugar de tiempo de compilación.
Steve Kuo
44
Correcto: la única forma de permitir la detección de tiempo de ejecución es subclasificar: PUEDE extender el tipo genérico y luego usar la declaración de búsqueda de reflejo de ese subtipo utilizado. Esto es un poco de reflexión, pero posible. Desafortunadamente, no hay una manera fácil de imponer que se debe usar una subclase genérica.
StaxMan
1
¿Seguramente stringList contiene cadenas y enteros de enteros? ¿Por qué hacerlo más complicado?
Ben Thurley
Respuestas:
408
Si esos son realmente campos de una determinada clase, puede obtenerlos con una pequeña ayuda de reflexión:
También puede hacerlo para los tipos de parámetros y el tipo de método de retorno.
Pero si están dentro del mismo alcance de la clase / método donde necesita saber sobre ellos, entonces no tiene sentido conocerlos, porque ya los ha declarado usted mismo.
@Oleg Su variable está usando <?>(tipo comodín) en lugar de <Class>(tipo de clase). Reemplace su <?>por <Class>o lo que sea <E>.
BalusC
19
También puede hacer lo mismo para los parámetros del método:
Type[] types = method.getGenericParameterTypes();//Now assuming that the first parameter to the method is of type List<Integer>ParameterizedType pType =(ParameterizedType) types[0];Class<?> clazz =(Class<?>) pType.getActualTypeArguments()[0];System.out.println(clazz);//prints out java.lang.Integer
En method.getGenericParameterTypes (); ¿Qué es el método?
Neo Ravi
18
Respuesta corta: no.
Probablemente sea un duplicado, no puedo encontrar uno apropiado en este momento.
Java usa algo llamado borrado de tipo, lo que significa que en tiempo de ejecución ambos objetos son equivalentes. El compilador sabe que las listas contienen enteros o cadenas y, como tal, pueden mantener un entorno de tipo seguro. Esta información se pierde (en base a una instancia de objeto) en tiempo de ejecución, y la lista solo contiene 'Objetos'.
PUEDE averiguar un poco sobre la clase, los tipos por los que podría parametrizarse, pero normalmente esto es cualquier cosa que extienda "Objeto", es decir, cualquier cosa. Si define un tipo como
class<A extendsMyClass>AClass{....}
AClass.class solo contendrá el hecho de que el parámetro A está limitado por MyClass, pero más que eso, no hay forma de saberlo.
Eso será cierto si no se especifica la clase concreta del genérico; en su ejemplo, declara explícitamente las listas como List<Integer>y List<String>; en esos casos, el borrado de tipo no se aplica.
Haroldo_OK
13
El tipo genérico de una colección solo debería importar si realmente tiene objetos, ¿verdad? Entonces, ¿no es más fácil simplemente hacer:
Collection<?> myCollection = getUnknownCollectionFromSomewhere();Class genericClass =null;Iterator it = myCollection.iterator();if(it.hasNext()){
genericClass = it.next().getClass();}if(genericClass !=null){//do whatever we needed to know the type for
No existe un tipo genérico en tiempo de ejecución, pero se garantiza que los objetos dentro del tiempo de ejecución serán del mismo tipo que el genérico declarado, por lo que es bastante fácil probar la clase del elemento antes de procesarlo.
Otra cosa que puede hacer es simplemente procesar la lista para obtener miembros que sean del tipo correcto, ignorando a otros (o procesándolos de manera diferente).
Map<Class<?>,List<Object>> classObjectMap = myCollection.stream().filter(Objects::nonNull).collect(Collectors.groupingBy(Object::getClass));// Process the list of the correct class, and/or handle objects of incorrect// class (throw exceptions, etc). You may need to group subclasses by// filtering the keys. For instance:List<Number> numbers = classObjectMap.entrySet().stream().filter(e->Number.class.isAssignableFrom(e.getKey())).flatMap(e->e.getValue().stream()).map(Number.class::cast).collect(Collectors.toList());
Esto le dará una lista de todos los elementos cuyas clases eran subclases de las Numbercuales luego puede procesar según lo necesite. El resto de los elementos se filtraron a otras listas. Debido a que están en el mapa, puede procesarlos como desee o ignorarlos.
Si desea ignorar por completo los elementos de otras clases, se vuelve mucho más simple:
Incluso puede crear un método de utilidad para asegurarse de que una lista contenga SOLAMENTE aquellos elementos que coincidan con una clase específica:
¿Y si tienes una colección vacía? ¿O si tiene una Colección <Object> con un Entero y una Cadena?
Falci
El contrato para la clase podría requerir que solo se pasen listas de objetos finales, y que la llamada al método devuelva nulo si la lista es nula, vacía o si el tipo de lista no es válido.
ggb667
1
En el caso de una colección vacía, es seguro asignar a cualquier tipo de lista en tiempo de ejecución, porque no hay nada en ella, y después de que se borra el tipo, una Lista es una Lista es una Lista. Debido a esto, no puedo pensar en ninguna instancia en la que el tipo de una colección vacía sea importante.
Steve K
Bueno, "podría importar" si mantienes la referencia en un hilo y la procesas una vez que los objetos comienzan a aparecer en un escenario de consumidor productor. Si la lista tuviera los tipos de objeto incorrectos, podrían suceder cosas malas. Supongo que para evitar que tenga que detener el procesamiento durmiendo hasta que haya al menos un elemento en la lista antes de continuar.
ggb667
Si tiene ese tipo de escenario de productor / consumidor, planear que la lista tenga un cierto tipo genérico sin estar seguro de lo que podría incluirse en la lista es, de todos modos, un mal diseño. En cambio, lo declararía como una colección de Objectsy cuando los retire de la lista, se los entregaría a un controlador apropiado según su tipo.
Si necesita obtener el tipo genérico de un tipo devuelto, utilicé este enfoque cuando necesitaba encontrar métodos en una clase que devolviera ay Collectionluego acceder a sus tipos genéricos:
/**
* Performs a forced cast.
* Returns null if the collection type does not match the items in the list.
* @param data The list to cast.
* @param listType The type of list to cast to.
*/static<T>List<?super T> castListSafe(List<?> data,Class<T> listType){List<T> retval =null;//This test could be skipped if you trust the callers, but it wouldn't be safe then.if(data!=null&&!data.isEmpty()&& listType.isInstance(data.iterator().next().getClass())){@SuppressWarnings("unchecked")//It's OK, we know List<T> contains the expected type.List<T> foo =(List<T>)data;return retval;}return retval;}Usage:protectedWhateverClass add(List<?> data){//For fluant useageif(data==null)|| data.isEmpty(){thrownewIllegalArgumentException("add() "+ data==null?"null":"empty"+" collection");}Class<?> colType = data.iterator().next().getClass();//Something
aMethod(castListSafe(data, colType));}
aMethod(List<Foo> foo){for(Foo foo:List){System.out.println(Foo);}}
aMethod(List<Bar> bar){for(Bar bar:List){System.out.println(Bar);}}
Los mismos problemas que con la respuesta de Steve K: ¿Qué pasa si la lista está vacía? ¿Qué sucede si la lista es de tipo <Object> que contiene una cadena y un entero? Su código significa que solo puede agregar más cadenas si el primer elemento de la lista es una cadena y no tiene ningún número entero ...
subrunner
Si la lista está vacía, no tienes suerte. Si la lista es Objeto y en realidad contiene un Objeto, está bien, pero si tiene una combinación de cosas, no tiene suerte. Borrar significa que esto es tan bueno como puedes hacer. Borra falta en mi opinión. Sus ramificaciones son a la vez poco entendidas e insuficientes para abordar los casos de uso interesantes y deseados de preocupación típica. Nada impide crear una clase a la que se le pueda preguntar qué tipo toma, pero simplemente no es así como funcionan las clases integradas. Las colecciones deberían saber lo que contienen, pero por desgracia ...
ggb667
4
En tiempo de ejecución, no, no puedes.
Sin embargo, a través de la reflexión, los parámetros de tipo son accesibles. Tratar
for(Field field :this.getDeclaredFields()){System.out.println(field.getGenericType())}
El método getGenericType()devuelve un objeto Type. En este caso, será una instancia de ParametrizedType, que a su vez tiene métodos getRawType()(que contendrá List.class, en este caso) y getActualTypeArguments(), que devolverá una matriz (en este caso, de longitud uno, que contiene uno String.classo Integer.class).
Tuve el mismo problema, pero usé instanceof en su lugar. Lo hizo de esta manera:
List<Object> listCheck =(List<Object>)(Object) stringList;if(!listCheck.isEmpty()){if(listCheck.get(0)instanceofString){System.out.println("List type is String");}if(listCheck.get(0)instanceofInteger){System.out.println("List type is Integer");}}}
Esto implica el uso de conversiones no controladas, así que solo haz esto cuando sepas que es una lista y de qué tipo puede ser.
Generalmente imposible, porque List<String>y List<Integer>comparte la misma clase de tiempo de ejecución.
Sin embargo, es posible que pueda reflexionar sobre el tipo declarado del campo que contiene la lista (si el tipo declarado no se refiere a un parámetro de tipo cuyo valor no conoce).
Como otros han dicho, la única respuesta correcta es no, el tipo ha sido borrado.
Si la lista tiene un número de elementos distinto de cero, puede investigar el tipo del primer elemento (utilizando su método getClass, por ejemplo). Eso no le dirá el tipo genérico de la lista, pero sería razonable suponer que el tipo genérico era una superclase de los tipos de la lista.
No recomendaría el enfoque, pero en un apuro podría ser útil.
Eso será cierto si no se especifica la clase concreta del genérico; en su ejemplo, declara explícitamente las listas como List<Integer>y List<String>; en esos casos, el borrado de tipo no se aplica.
Haroldo_OK
2
import org.junit.Assert;import org.junit.Test;import java.lang.reflect.Field;import java.lang.reflect.ParameterizedType;import java.util.ArrayList;import java.util.Collection;import java.util.List;publicclassGenericTypeOfCollectionTest{publicclassFormBean{}publicclassMyClazz{privateList<FormBean> list =newArrayList<FormBean>();}@Testpublicvoid testName()throwsException{Field[] fields =MyClazz.class.getFields();for(Field field : fields){//1. Check if field is of Collection Typeif(Collection.class.isAssignableFrom(field.getType())){//2. Get Generic type of your fieldClass fieldGenericType = getFieldGenericType(field);//3. Compare with <FromBean>Assert.assertTrue("List<FormBean>",FormBean.class.isAssignableFrom(fieldGenericType));}}}//Returns generic type of any fieldpublicClass getFieldGenericType(Field field){if(ParameterizedType.class.isAssignableFrom(field.getGenericType().getClass())){ParameterizedType genericType =(ParameterizedType) field.getGenericType();return((Class)(genericType.getActualTypeArguments()[0])).getSuperclass();}//Returns dummy Boolean Class to compare with ValueObject & FormBeanreturnnewBoolean(false).getClass();}}
Eso será cierto si no se especifica la clase concreta del genérico; en su ejemplo, declara explícitamente las listas como List<Integer>y List<String>; en esos casos, el borrado de tipo no se aplica.
Haroldo_OK
0
Use Reflection para obtener Fieldestos, entonces simplemente puede hacer: field.genericTypepara obtener el tipo que contiene la información sobre genéricos también.
Respuestas:
Si esos son realmente campos de una determinada clase, puede obtenerlos con una pequeña ayuda de reflexión:
También puede hacerlo para los tipos de parámetros y el tipo de método de retorno.
Pero si están dentro del mismo alcance de la clase / método donde necesita saber sobre ellos, entonces no tiene sentido conocerlos, porque ya los ha declarado usted mismo.
fuente
<?>
(tipo comodín) en lugar de<Class>
(tipo de clase). Reemplace su<?>
por<Class>
o lo que sea<E>
.También puede hacer lo mismo para los parámetros del método:
fuente
Respuesta corta: no.
Probablemente sea un duplicado, no puedo encontrar uno apropiado en este momento.
Java usa algo llamado borrado de tipo, lo que significa que en tiempo de ejecución ambos objetos son equivalentes. El compilador sabe que las listas contienen enteros o cadenas y, como tal, pueden mantener un entorno de tipo seguro. Esta información se pierde (en base a una instancia de objeto) en tiempo de ejecución, y la lista solo contiene 'Objetos'.
PUEDE averiguar un poco sobre la clase, los tipos por los que podría parametrizarse, pero normalmente esto es cualquier cosa que extienda "Objeto", es decir, cualquier cosa. Si define un tipo como
AClass.class solo contendrá el hecho de que el parámetro A está limitado por MyClass, pero más que eso, no hay forma de saberlo.
fuente
List<Integer>
yList<String>
; en esos casos, el borrado de tipo no se aplica.El tipo genérico de una colección solo debería importar si realmente tiene objetos, ¿verdad? Entonces, ¿no es más fácil simplemente hacer:
No existe un tipo genérico en tiempo de ejecución, pero se garantiza que los objetos dentro del tiempo de ejecución serán del mismo tipo que el genérico declarado, por lo que es bastante fácil probar la clase del elemento antes de procesarlo.
Otra cosa que puede hacer es simplemente procesar la lista para obtener miembros que sean del tipo correcto, ignorando a otros (o procesándolos de manera diferente).
Esto le dará una lista de todos los elementos cuyas clases eran subclases de las
Number
cuales luego puede procesar según lo necesite. El resto de los elementos se filtraron a otras listas. Debido a que están en el mapa, puede procesarlos como desee o ignorarlos.Si desea ignorar por completo los elementos de otras clases, se vuelve mucho más simple:
Incluso puede crear un método de utilidad para asegurarse de que una lista contenga SOLAMENTE aquellos elementos que coincidan con una clase específica:
fuente
Object
sy cuando los retire de la lista, se los entregaría a un controlador apropiado según su tipo.Para encontrar el tipo genérico de un campo:
fuente
Si necesita obtener el tipo genérico de un tipo devuelto, utilicé este enfoque cuando necesitaba encontrar métodos en una clase que devolviera ay
Collection
luego acceder a sus tipos genéricos:Esto produce:
fuente
Ampliando la respuesta de Steve K:
fuente
En tiempo de ejecución, no, no puedes.
Sin embargo, a través de la reflexión, los parámetros de tipo son accesibles. Tratar
El método
getGenericType()
devuelve un objeto Type. En este caso, será una instancia deParametrizedType
, que a su vez tiene métodosgetRawType()
(que contendráList.class
, en este caso) ygetActualTypeArguments()
, que devolverá una matriz (en este caso, de longitud uno, que contiene unoString.class
oInteger.class
).fuente
method.getGenericParameterTypes()
para obtener los tipos declarados de los parámetros de un método.Tuve el mismo problema, pero usé instanceof en su lugar. Lo hizo de esta manera:
Esto implica el uso de conversiones no controladas, así que solo haz esto cuando sepas que es una lista y de qué tipo puede ser.
fuente
Generalmente imposible, porque
List<String>
yList<Integer>
comparte la misma clase de tiempo de ejecución.Sin embargo, es posible que pueda reflexionar sobre el tipo declarado del campo que contiene la lista (si el tipo declarado no se refiere a un parámetro de tipo cuyo valor no conoce).
fuente
Como otros han dicho, la única respuesta correcta es no, el tipo ha sido borrado.
Si la lista tiene un número de elementos distinto de cero, puede investigar el tipo del primer elemento (utilizando su método getClass, por ejemplo). Eso no le dirá el tipo genérico de la lista, pero sería razonable suponer que el tipo genérico era una superclase de los tipos de la lista.
No recomendaría el enfoque, pero en un apuro podría ser útil.
fuente
List<Integer>
yList<String>
; en esos casos, el borrado de tipo no se aplica.fuente
getFieldGenericTypefieldGenericType
que no se puede encontrarEl tipo se borra, por lo que no podrá hacerlo. Ver http://en.wikipedia.org/wiki/Type_erasure y http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure
fuente
List<Integer>
yList<String>
; en esos casos, el borrado de tipo no se aplica.Use Reflection para obtener
Field
estos, entonces simplemente puede hacer:field.genericType
para obtener el tipo que contiene la información sobre genéricos también.fuente