import com.fasterxml.jackson.core.type.TypeReference; new TypeReference<T>(){}
Bogdan Shulga
Respuestas:
569
La respuesta corta es que no hay forma de averiguar el tipo de tiempo de ejecución de los parámetros de tipo genérico en Java. Sugiero leer el capítulo sobre borrado de tipos en el Tutorial de Java para obtener más detalles.
Una solución popular para esto es pasar el Classparámetro del tipo al constructor del tipo genérico, p. Ej.
classFoo<T>{finalClass<T> typeParameterClass;publicFoo(Class<T> typeParameterClass){this.typeParameterClass = typeParameterClass;}publicvoid bar(){// you can access the typeParameterClass here and do whatever you like}}
Esta respuesta proporciona una solución válida, pero es incorrecto decir que no hay forma de encontrar el tipo genérico en tiempo de ejecución. Resulta que el borrado tipo es mucho más complejo que el borrado general. Mi respuesta muestra cómo obtener las clases de tipo genérico.
Ben Thurley
3
Truco de @BenThurley Neat, pero por lo que puedo ver, solo funciona si hay un supertipo genérico para que lo use. En mi ejemplo, no puede recuperar el tipo de T en Foo <T>.
Zsolt Török
@webjockey No, no deberías. Asignar typeParameterClasssin una asignación predeterminada en el constructor está perfectamente bien. No es necesario configurarlo por segunda vez.
Adowrath
Esta es la primera solución que viene a la mente, pero a veces no eres tú quien creará / iniciará objetos. Entonces no podrás usar el constructor. Por ejemplo, al recuperar entidades JPA de la base de datos.
Paramvir Singh Karwal
233
Estaba buscando una manera de hacerlo yo mismo sin agregar una dependencia adicional al classpath. Después de investigar un poco, descubrí que es posible siempre que tenga un supertipo genérico. Esto estuvo bien para mí ya que estaba trabajando con una capa DAO con un supertipo de capa genérica. Si esto se ajusta a su escenario, entonces es el enfoque más perfecto en mi humilde opinión.
La mayoría de los casos de uso de genéricos que he encontrado tienen algún tipo de supertipo genérico, por ejemplo, List<T>para ArrayList<T>o GenericDAO<T>para DAO<T>, etc.
Mi proyecto estaba usando Spring, que es aún mejor, ya que Spring tiene un útil método de utilidad para encontrar el tipo. Este es el mejor enfoque para mí, ya que se ve más limpio. Supongo que si no estuvieras usando Spring podrías escribir tu propio método de utilidad.
import org.springframework.core.GenericTypeResolver;publicabstractclassAbstractHibernateDao<T extendsDomainObject>implementsDataAccessObject<T>{@AutowiredprivateSessionFactory sessionFactory;privatefinalClass<T> genericType;privatefinalString RECORD_COUNT_HQL;privatefinalString FIND_ALL_HQL;@SuppressWarnings("unchecked")publicAbstractHibernateDao(){this.genericType =(Class<T>)GenericTypeResolver.resolveTypeArgument(getClass(),AbstractHibernateDao.class);this.RECORD_COUNT_HQL ="select count(*) from "+this.genericType.getName();this.FIND_ALL_HQL ="from "+this.genericType.getName()+" t ";}
Por favor, aclarar el significado de resolveTypeArgument argumentos
gstackoverflow
getClass () es un método de java.lang.Object que devolverá la clase del objeto específico en tiempo de ejecución, este es el objeto para el que desea resolver el tipo. AbstractHibernateDao.class es solo el nombre de la clase base o superclase de la jerarquía de clases de tipo genérico. La declaración de importación está incluida, por lo que debería poder encontrar fácilmente los documentos y comprobarlo usted mismo. Esta es la página docs.spring.io/spring/docs/current/javadoc-api/org/…
Ben Thurley
¿Cuál es la solución de primavera con la versión 4.3.6 y superior? No funciona con la primavera 4.3.6.
@ AlikElzin-kilaka se inicializa en el constructor utilizando la clase Spring GenericTypeResolver.
Ben Thurley
103
Sin embargo, hay una pequeña laguna: si define su Fooclase como abstracta. Eso significaría que debe crear una instancia de su clase como:
Foo<MyType> myFoo =newFoo<MyType>(){};
(Tenga en cuenta las llaves dobles al final).
Ahora puede recuperar el tipo de Ten tiempo de ejecución:
Type mySuperclass = myFoo.getClass().getGenericSuperclass();Type tType =((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
Sin embargo, mySuperclasstenga en cuenta que tiene que ser la superclase de la definición de clase que realmente define el tipo final paraT .
Tampoco es muy elegante, pero debe decidir si prefiere new Foo<MyType>(){}o new Foo<MyType>(MyType.class);en su código.
Por ejemplo:
import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.util.ArrayDeque;import java.util.Deque;import java.util.NoSuchElementException;/**
* Captures and silently ignores stack exceptions upon popping.
*/publicabstractclassSilentStack<E>extendsArrayDeque<E>{public E pop(){try{returnsuper.pop();}catch(NoSuchElementException nsee ){return create();}}public E create(){try{Type sooper = getClass().getGenericSuperclass();Type t =((ParameterizedType)sooper).getActualTypeArguments()[0];return(E)(Class.forName( t.toString()).newInstance());}catch(Exception e ){returnnull;}}}
Entonces:
publicclassMain{// Note the braces...privateDeque<String> stack =newSilentStack<String>(){};publicstaticvoid main(String args[]){// Returns a new instance of String.String s = stack.pop();System.out.printf("s = '%s'\n", s );}}
¡Esta es fácilmente la mejor respuesta aquí! Además, por lo que vale, esta es la estrategia que utiliza Google Guice para vincular clases conTypeLiteral
ATG
14
Tenga en cuenta que cada vez que se utiliza este método de construcción de objetos, se crea una nueva clase anónima. En otras palabras, dos objetos ay bcreados de esta manera extenderán la misma clase pero no tendrán clases de instancia idénticas. a.getClass() != b.getClass()
Martin Serrano
3
Hay un escenario en el que esto no funciona. Si Foo implementara una interfaz, como Serializable, entonces la clase anónima no sería Serializable a menos que la instancia de la clase lo sea. Intenté solucionarlo creando una clase de fábrica serializable que crea la clase anónima derivada de Foo, pero luego, por alguna razón, getActualTypeArguments devuelve el tipo genérico en lugar de la clase real. Por ejemplo: (nuevo FooFactory <MyType> ()). CreateFoo ()
Lior Chaga
38
Un enfoque / solución / solución estándar es agregar un classobjeto a los constructores, como:
publicclassFoo<T>{privateClass<T> type;publicFoo(Class<T> type){this.type = type;}publicClass<T> getType(){return type;}public T newInstance(){return type.newInstance();}}
Pero parece que no se puede @autowired en el uso real, alguna forma de evitarlo?
Alfred Huang
@AlfredHuang La solución sería crear un bean para la clase que haga esto y no depender del cableado automático.
Calebj
20
Imagine que tiene una superclase abstracta que es genérica:
publicabstractclassFoo<?extends T>{}
Y luego tienes una segunda clase que extiende Foo con una barra genérica que extiende T:
publicclassSecondextendsFoo<Bar>{}
Puede obtener la clase Bar.classen la clase Foo seleccionando Type(de bert bruynooghe answer) e inferiéndola usando la Classinstancia:
Type mySuperclass = myFoo.getClass().getGenericSuperclass();Type tType =((ParameterizedType)mySuperclass).getActualTypeArguments()[0];//Parse it as StringString className = tType.toString().split(" ")[1];Class clazz =Class.forName(className);
Debe tener en cuenta que esta operación no es ideal, por lo que es una buena idea almacenar en caché el valor calculado para evitar múltiples cálculos al respecto. Uno de los usos típicos es en la implementación genérica de DAO.
toString().split(" ")[1]ese era el problema, evita el"class "
IgniteCoders
16
Aquí hay una solución de trabajo:
@SuppressWarnings("unchecked")privateClass<T> getGenericTypeClass(){try{String className =((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();Class<?> clazz =Class.forName(className);return(Class<T>) clazz;}catch(Exception e){thrownewIllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");}}
NOTAS:
Solo se puede usar como superclase
Tiene que extenderse con la clase escrita ( Child extends Generic<Integer>)
O
Tiene que ser creado como implementación anónima ( new Generic<Integer>() {};)
Una ruta mejor que la Clase sugerida por los demás es pasar un objeto que pueda hacer lo que hubiera hecho con la Clase, por ejemplo, crear una nueva instancia.
interfaceFactory<T>{
T apply();}<T>voidList<T> make10(Factory<T> factory){List<T> result =newArrayList<T>();for(int a =0; a <10; a++)
result.add(factory.apply());return result;}classFooFactory<T>implementsFactory<Foo<T>>{publicFoo<T> apply(){returnnewFoo<T>();}}List<Foo<Integer>> foos = make10(newFooFactory<Integer>());
@ Ricky Clarkson: No veo cómo esta fábrica debería devolver foos parametrizados. ¿Podría explicar cómo saca a Foo <T> de esto? Me parece que esto solo da Foo no parametrizado. ¿No es la T en make10 simplemente Foo aquí?
ib84
@ ib84 He arreglado el código; Parece que me perdí que Foo fue parametrizado cuando escribí la respuesta originalmente.
Ricky Clarkson
9
Tuve este problema en una clase genérica abstracta. En este caso particular, la solución es más simple:
Sí, lo es, pero me gustaría dejar un mínimo que debe hacerse al extender esta clase. Verifique la respuesta de
droidpl
5
Tengo una solución (fea pero efectiva) para este problema, que utilicé recientemente:
import java.lang.reflect.TypeVariable;publicstatic<T>Class<T> getGenericClass(){
__<T> ins =new __<T>();TypeVariable<?>[] cls = ins.getClass().getTypeParameters();return(Class<T>)cls[0].getClass();}privatefinalclass __<T>// generic helper class which does only provide type information{private __(){}}
Encontré una forma genérica y simple de hacerlo. En mi clase, creé un método que devuelve el tipo genérico de acuerdo con su posición en la definición de clase. Supongamos una definición de clase como esta:
publicclassMyClass<A, B, C>{}
Ahora creemos algunos atributos para persistir los tipos:
publicclassMyClass<A, B, C>{privateClass<A> aType;privateClass<B> bType;privateClass<C> cType;// Getters and setters (not necessary if you are going to use them internally)}
Luego puede crear un método genérico que devuelva el tipo basado en el índice de la definición genérica:
/**
* Returns a {@link Type} object to identify generic types
* @return type
*/privateType getGenericClassType(int index){// To make it use generics without supplying the class typeType type = getClass().getGenericSuperclass();while(!(type instanceofParameterizedType)){if(type instanceofParameterizedType){
type =((Class<?>)((ParameterizedType) type).getRawType()).getGenericSuperclass();}else{
type =((Class<?>) type).getGenericSuperclass();}}return((ParameterizedType) type).getActualTypeArguments()[index];}
Finalmente, en el constructor simplemente llame al método y envíe el índice para cada tipo. El código completo debería verse así:
publicclassMyClass<A, B, C>{privateClass<A> aType;privateClass<B> bType;privateClass<C> cType;publicMyClass(){this.aType =(Class<A>) getGenericClassType(0);this.bType =(Class<B>) getGenericClassType(1);this.cType =(Class<C>) getGenericClassType(2);}/**
* Returns a {@link Type} object to identify generic types
* @return type
*/privateType getGenericClassType(int index){Type type = getClass().getGenericSuperclass();while(!(type instanceofParameterizedType)){if(type instanceofParameterizedType){
type =((Class<?>)((ParameterizedType) type).getRawType()).getGenericSuperclass();}else{
type =((Class<?>) type).getGenericSuperclass();}}return((ParameterizedType) type).getActualTypeArguments()[index];}}
Como se explica en otras respuestas, para usar esto ParameterizedType enfoque, necesita extender la clase, pero eso parece un trabajo extra para hacer una clase completamente nueva que la extienda ...
Entonces, hacer que la clase sea abstracta te obliga a extenderla, satisfaciendo así el requisito de subclasificación. (usando lombok's @Getter).
public<T> T yourMethodSignature(Class<T> type){// get some object and check the type match the given typeObject result =...if(type.isAssignableFrom(result.getClass())){return(T)result;}else{// handle the error}}
Si está ampliando o implementando una clase / interfaz que usa genéricos, puede obtener el Tipo genérico de clase / interfaz principal, sin modificar ninguna clase / interfaz existente.
Podría haber tres posibilidades,
Caso 1
Cuando su clase está extendiendo una clase que está utilizando Genéricos
Eso es bastante sencillo. Si necesita desde dentro de la misma clase:
Class clazz =this.getClass();ParameterizedType parameterizedType =(ParameterizedType) clazz.getGenericSuperclass();try{Class typeClass =Class.forName( parameterizedType.getActualTypeArguments()[0].getTypeName());// You have the instance of type 'T' in typeClass variableSystem.out.println("Class instance name: "+ typeClass.getName());}catch(ClassNotFoundException e){System.out.println("ClassNotFound!! Something wrong! "+ e.getMessage());}
En realidad, supongo que tiene un campo en su clase de tipo T. Si no hay un campo de tipo T, ¿de qué sirve tener un tipo genérico? Entonces, simplemente puede hacer una instancia de en ese campo.
En mi caso, tengo un
Listar <T> elementos;
en mi clase, y compruebo si el tipo de clase es "Localidad" por
if (items.get (0) instanceof Locality) ...
Por supuesto, esto solo funciona si el número total de clases posibles es limitado.
classGenericClass<T>(private val rawType:Class<*>){
constructor():this(`$Gson$Types`.getRawType(object :TypeToken<T>(){}.getType()))
fun getRawType():Class<T>{return rawType as Class<T>}}
Quería pasar T.class a un método que utilizara genéricos
El método readFile lee un archivo .csv especificado por fileName con fullpath. Puede haber archivos csv con diferentes contenidos, por lo tanto, necesito pasar la clase de archivo de modelo para poder obtener los objetos apropiados. Como se trata de leer un archivo csv, quería hacerlo de forma genérica. Por alguna razón u otra, ninguna de las soluciones anteriores funcionó para mí. Necesito usar
Class<? extends T> typepara que funcione. Yo uso la biblioteca opencsv para analizar los archivos CSV.
import com.fasterxml.jackson.core.type.TypeReference;
new TypeReference<T>(){}
Respuestas:
La respuesta corta es que no hay forma de averiguar el tipo de tiempo de ejecución de los parámetros de tipo genérico en Java. Sugiero leer el capítulo sobre borrado de tipos en el Tutorial de Java para obtener más detalles.
Una solución popular para esto es pasar el
Class
parámetro del tipo al constructor del tipo genérico, p. Ej.fuente
typeParameterClass
sin una asignación predeterminada en el constructor está perfectamente bien. No es necesario configurarlo por segunda vez.Estaba buscando una manera de hacerlo yo mismo sin agregar una dependencia adicional al classpath. Después de investigar un poco, descubrí que es posible siempre que tenga un supertipo genérico. Esto estuvo bien para mí ya que estaba trabajando con una capa DAO con un supertipo de capa genérica. Si esto se ajusta a su escenario, entonces es el enfoque más perfecto en mi humilde opinión.
La mayoría de los casos de uso de genéricos que he encontrado tienen algún tipo de supertipo genérico, por ejemplo,
List<T>
paraArrayList<T>
oGenericDAO<T>
paraDAO<T>
, etc.Solución pura de Java
El artículo Acceso a tipos genéricos en tiempo de ejecución en Java explica cómo puede hacerlo utilizando Java puro.
Solución de primavera
Mi proyecto estaba usando Spring, que es aún mejor, ya que Spring tiene un útil método de utilidad para encontrar el tipo. Este es el mejor enfoque para mí, ya que se ve más limpio. Supongo que si no estuvieras usando Spring podrías escribir tu propio método de utilidad.
fuente
Sin embargo, hay una pequeña laguna: si define su
Foo
clase como abstracta. Eso significaría que debe crear una instancia de su clase como:(Tenga en cuenta las llaves dobles al final).
Ahora puede recuperar el tipo de
T
en tiempo de ejecución:Sin embargo,
mySuperclass
tenga en cuenta que tiene que ser la superclase de la definición de clase que realmente define el tipo final paraT
.Tampoco es muy elegante, pero debe decidir si prefiere
new Foo<MyType>(){}
onew Foo<MyType>(MyType.class);
en su código.Por ejemplo:
Entonces:
fuente
TypeLiteral
a
yb
creados de esta manera extenderán la misma clase pero no tendrán clases de instancia idénticas.a.getClass() != b.getClass()
Un enfoque / solución / solución estándar es agregar un
class
objeto a los constructores, como:fuente
Imagine que tiene una superclase abstracta que es genérica:
Y luego tienes una segunda clase que extiende Foo con una barra genérica que extiende T:
Puede obtener la clase
Bar.class
en la clase Foo seleccionandoType
(de bert bruynooghe answer) e inferiéndola usando laClass
instancia:Debe tener en cuenta que esta operación no es ideal, por lo que es una buena idea almacenar en caché el valor calculado para evitar múltiples cálculos al respecto. Uno de los usos típicos es en la implementación genérica de DAO.
La implementación final:
El valor devuelto es Bar.class cuando se invoca desde la clase Foo en otra función o desde la clase Bar.
fuente
toString().split(" ")[1]
ese era el problema, evita el"class "
Aquí hay una solución de trabajo:
NOTAS: Solo se puede usar como superclase
Child extends Generic<Integer>
)O
new Generic<Integer>() {};
)fuente
No puede hacerlo debido al tipo de borrado. Consulte también la pregunta de desbordamiento de pila genéricos de Java - borrado de tipo - cuándo y qué sucede .
fuente
Una ruta mejor que la Clase sugerida por los demás es pasar un objeto que pueda hacer lo que hubiera hecho con la Clase, por ejemplo, crear una nueva instancia.
fuente
Tuve este problema en una clase genérica abstracta. En este caso particular, la solución es más simple:
y luego en la clase derivada:
fuente
Tengo una solución (fea pero efectiva) para este problema, que utilicé recientemente:
fuente
Es posible:
Necesita dos funciones de hibernate-generic-dao / blob / master / dao / src / main / java / com / googlecode / genericdao / dao / DAOUtil.java .
Para obtener más explicaciones, consulte Genéricos reflectantes .
fuente
Encontré una forma genérica y simple de hacerlo. En mi clase, creé un método que devuelve el tipo genérico de acuerdo con su posición en la definición de clase. Supongamos una definición de clase como esta:
Ahora creemos algunos atributos para persistir los tipos:
Luego puede crear un método genérico que devuelva el tipo basado en el índice de la definición genérica:
Finalmente, en el constructor simplemente llame al método y envíe el índice para cada tipo. El código completo debería verse así:
fuente
Como se explica en otras respuestas, para usar esto
ParameterizedType
enfoque, necesita extender la clase, pero eso parece un trabajo extra para hacer una clase completamente nueva que la extienda ...Entonces, hacer que la clase sea abstracta te obliga a extenderla, satisfaciendo así el requisito de subclasificación. (usando lombok's @Getter).
Ahora para extenderlo sin definir una nueva clase. (Tenga en cuenta el {} al final ... extendido, pero no sobrescriba nada, a menos que lo desee).
fuente
Supongo que, dado que tiene una clase genérica, tendría una variable como esa:
(esta variable necesita tomar un valor en el constructor)
En ese caso, simplemente puede crear el siguiente método:
¡Espero eso ayude!
fuente
fuente
Si está ampliando o implementando una clase / interfaz que usa genéricos, puede obtener el Tipo genérico de clase / interfaz principal, sin modificar ninguna clase / interfaz existente.
Podría haber tres posibilidades,
Caso 1 Cuando su clase está extendiendo una clase que está utilizando Genéricos
Caso 2 Cuando su clase está implementando una interfaz que está usando Genéricos
Caso 3 Cuando su interfaz está extendiendo una interfaz que está usando Genéricos
fuente
Eso es bastante sencillo. Si necesita desde dentro de la misma clase:
fuente
En realidad, supongo que tiene un campo en su clase de tipo T. Si no hay un campo de tipo T, ¿de qué sirve tener un tipo genérico? Entonces, simplemente puede hacer una instancia de en ese campo.
En mi caso, tengo un
en mi clase, y compruebo si el tipo de clase es "Localidad" porPor supuesto, esto solo funciona si el número total de clases posibles es limitado.
fuente
Esta pregunta es antigua, pero ahora lo mejor es usar google
Gson
.Un ejemplo para personalizar
viewModel
.Clase de tipo genérico
fuente
Quería pasar T.class a un método que utilizara genéricos
El método readFile lee un archivo .csv especificado por fileName con fullpath. Puede haber archivos csv con diferentes contenidos, por lo tanto, necesito pasar la clase de archivo de modelo para poder obtener los objetos apropiados. Como se trata de leer un archivo csv, quería hacerlo de forma genérica. Por alguna razón u otra, ninguna de las soluciones anteriores funcionó para mí. Necesito usar
Class<? extends T> type
para que funcione. Yo uso la biblioteca opencsv para analizar los archivos CSV.Así es como se llama al método readFile
fuente
Estoy usando una solución para esto:
fuente