type - java generics parameter
Obtener tipo genérico de java.util.List (14)
Yo tengo;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
¿Hay una manera (fácil) de recuperar el tipo genérico de la lista?
Ampliando la respuesta de Steve K:
/**
* 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:
protected WhateverClass add(List<?> data) {//For fluant useage
if(data==null) || data.isEmpty(){
throw new IllegalArgumentException("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);
}
}
Como han dicho otros, la única respuesta correcta es no, el tipo se ha 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 en la lista.
No abogaría por el enfoque, pero en un vínculo podría ser útil.
El tipo genérico de una colección solo debería importar si realmente tiene objetos, ¿verdad? Entonces, ¿no es más fácil hacer simplemente?
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 que se encuentran en el tiempo de ejecución son del mismo tipo que el genérico declarado, por lo que es bastante fácil probar la clase del elemento antes de procesarlo.
El tipo se borra para que no pueda hacerlo. Consulte http://en.wikipedia.org/wiki/Type_erasure y http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure
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 los métodos getRawType()
(que contendrán List.class
, en este caso) y getActualTypeArguments()
, que devolverá una matriz (en este caso, de longitud uno , que contiene String.class
o Integer.class
).
Generalmente imposible, porque List<String>
y List<Integer>
comparten 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).
Para encontrar el tipo genérico de un campo:
((Class)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]).getSimpleName()
Respuesta corta: no.
Probablemente este 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 tales pueden mantener un entorno seguro de tipo. Esta información se pierde (en una instancia de objeto) en tiempo de ejecución, y la lista solo contiene ''Objetos''.
PUEDE descubrir un poco sobre la clase, qué tipos pueden ser parametrizados, pero normalmente esto es solo algo que extiende "Objeto", es decir, cualquier cosa. Si define un tipo como
class <A extends MyClass> 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.
Si esos son realmente campos de cierta clase, entonces puedes obtenerlos con una pequeña ayuda de reflexión:
package test;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
public class Test {
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
public static void main(String... args) throws Exception {
Field stringListField = Test.class.getDeclaredField("stringList");
ParameterizedType stringListType = (ParameterizedType) stringListField.getGenericType();
Class<?> stringListClass = (Class<?>) stringListType.getActualTypeArguments()[0];
System.out.println(stringListClass); // class java.lang.String.
Field integerListField = Test.class.getDeclaredField("integerList");
ParameterizedType integerListType = (ParameterizedType) integerListField.getGenericType();
Class<?> integerListClass = (Class<?>) integerListType.getActualTypeArguments()[0];
System.out.println(integerListClass); // class java.lang.Integer.
}
}
También puede hacer eso para los tipos de parámetros y devolver el tipo de métodos.
Pero si están dentro del mismo ámbito de la clase / método en el que necesita saber sobre ellos, entonces no tiene sentido conocerlos, porque ya los ha declarado usted mismo.
Si necesita obtener el tipo genérico de un tipo devuelto, utilicé este enfoque cuando necesitaba encontrar métodos en una clase que devolviera una Collection
y luego accediera a sus tipos genéricos:
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
public class Test {
public List<String> test() {
return null;
}
public static void main(String[] args) throws Exception {
for (Method method : Test.class.getMethods()) {
Class returnClass = method.getReturnType();
if (Collection.class.isAssignableFrom(returnClass)) {
Type returnType = method.getGenericReturnType();
if (returnType instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) returnType;
Type[] argTypes = paramType.getActualTypeArguments();
if (argTypes.length > 0) {
System.out.println("Generic type is " + argTypes[0]);
}
}
}
}
}
}
Esto produce:
El tipo genérico es la clase java.lang.String.
También puedes hacer lo mismo con 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
Tenía el mismo problema, pero utilicé instanceof en su lugar. Lo hice de esta manera:
List<Object> listCheck = (List<Object>)(Object) stringList;
if (!listCheck.isEmpty()) {
if (listCheck.get(0) instanceof String) {
System.out.println("List type is String");
}
if (listCheck.get(0) instanceof Integer) {
System.out.println("List type is Integer");
}
}
}
Esto implica el uso de conversiones sin marcar, así que solo haga esto cuando sepa que es una lista y de qué tipo puede ser.
Use Reflexión para obtener el Field
para estos y luego puede hacer: field.genericType
para obtener el tipo que contiene la información sobre genéricos también.
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;
public class GenericTypeOfCollectionTest {
public class FormBean {
}
public class MyClazz {
private List<FormBean> list = new ArrayList<FormBean>();
}
@Test
public void testName() throws Exception {
Field[] fields = MyClazz.class.getFields();
for (Field field : fields) {
//1. Check if field is of Collection Type
if (Collection.class.isAssignableFrom(field.getType())) {
//2. Get Generic type of your field
Class fieldGenericType = getFieldGenericType(field);
//3. Compare with <FromBean>
Assert.assertTrue("List<FormBean>",
FormBean.class.isAssignableFrom(getFieldGenericTypefieldGenericType));
}
}
}
//Returns generic type of any field
public Class 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 & FormBean
return new Boolean(false).getClass();
}
}