J'ai;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
Existe-t-il un moyen (facile) de récupérer le type générique de la liste?
Si ce sont en fait des champs d’une certaine classe, vous pouvez les obtenir avec un peu d’aide à la réflexion:
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.
}
}
Vous pouvez également le faire pour les types de paramètres et le type de méthodes retourné.
Mais s'ils se trouvent dans le même champ d'application de la classe/méthode où vous devez les connaître, il est alors inutile de les connaître, car vous les avez déjà déclarés vous-même.
Vous pouvez également faire de même pour les paramètres de méthode:
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
Réponse courte: non.
Ceci est probablement une copie, je ne peux pas en trouver un pour le moment.
Java utilise quelque chose appelé type effacement, ce qui signifie qu'au moment de l'exécution, les deux objets sont équivalents. Le compilateur sait que les listes contiennent des entiers ou des chaînes et peuvent donc maintenir un environnement sans risque. Ces informations sont perdues (sur une base d'objet) au moment de l'exécution et la liste ne contient que des "objets".
Vous POUVEZ en savoir un peu plus sur la classe, sur quels types elle peut être paramétrée, mais normalement, il s’agit de tout ce qui étend "Object", c’est-à-dire n'importe quoi. Si vous définissez un type comme
class <A extends MyClass> AClass {....}
AClass.class ne contiendra que le fait que le paramètre A soit lié par MyClass, mais il n’ya pas de moyen de le savoir.
Si vous devez obtenir le type générique d'un type renvoyé, j'ai utilisé cette approche lorsque je devais rechercher des méthodes dans une classe qui renvoyait une Collection
, puis accéder à leurs types génériques:
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]);
}
}
}
}
}
}
Cela génère:
Le type générique est la classe Java.lang.String
Pour trouver le type générique d'un champ:
((Class)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]).getSimpleName()
Le type générique d'une collection ne devrait avoir d'importance que si elle contient réellement des objets, n'est-ce pas? Donc, n'est-il pas plus facile de simplement faire:
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
Le type générique n'existe pas au moment de l'exécution, mais les objets qu'il contient sont assurément du même type que le générique déclaré. Il est donc assez simple de tester la classe de l'élément avant de la traiter.
Développer la réponse 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);
}
}
Avait le même problème, mais j'ai utilisé instanceof à la place. Est-ce que c'est comme ça:
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");
}
}
}
Cela implique d’utiliser des conversions non vérifiées, alors ne le faites que lorsque vous savez que c’est une liste et quel type elle peut être.
Au moment de l'exécution, non, vous ne pouvez pas.
Cependant, par réflexion, les paramètres de type sont accessibles. Essayer
for(Field field : this.getDeclaredFields()) {
System.out.println(field.getGenericType())
}
La méthode getGenericType()
renvoie un objet Type. Dans ce cas, il s'agira d'une instance de ParametrizedType
, qui à son tour utilise les méthodes getRawType()
(qui contiendront List.class
, dans ce cas) et getActualTypeArguments()
, qui renverront un tableau (dans ce cas, de longueur un, contenant soit String.class
ou Integer.class
).
Généralement impossible, car List<String>
et List<Integer>
partagent la même classe d'exécution.
Vous pourrez cependant réfléchir au type déclaré du champ contenant la liste (si le type déclaré ne fait pas référence à un paramètre de type dont vous ne connaissez pas la valeur).
Comme d'autres l'ont dit, la seule réponse correcte est non, le type a été effacé.
Si la liste comporte un nombre d'éléments non nul, vous pouvez rechercher le type du premier élément (à l'aide de sa méthode getClass, par exemple). Cela ne vous indiquera pas le type générique de la liste, mais il serait raisonnable de supposer que le type générique était une superclasse des types de la liste.
Je ne préconiserais pas l'approche, mais dans une impasse, cela pourrait être utile.
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(fieldGenericType));
}
}
}
//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();
}
}
Utilisez Reflection pour obtenir la Field
, vous pouvez simplement faire: field.genericType
pour obtenir le type contenant les informations sur le générique.
Le type est effacé pour que vous ne puissiez pas le faire. Voir http://en.wikipedia.org/wiki/Type_erasure et http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure