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?
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?
Réponses:
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 renvoyer le type de méthodes.
Mais s'ils se trouvent dans le même champ d'application de la classe / méthode que vous devez les connaître, il est inutile de les connaître, car vous les avez déjà déclarés vous-même.
<?>
(type générique) au lieu de <Class>
(type classe). Remplacez votre <?>
par <Class>
ou autre chose <E>
.
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.
Il s'agit probablement d'un doublon, impossible d'en trouver un en ce moment.
Java utilise quelque chose appelé effacement de type, 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, à ce titre, peuvent maintenir un environnement sûr de type. Ces informations sont perdues (sur la base d'une instance 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, par quels types elle pourrait être paramétrée, mais normalement c'est juste 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 contiendra uniquement le fait que le paramètre A est délimité par MyClass, mais plus que cela, il n'y a aucun moyen de le savoir.
List<Integer>
et List<String>
; dans ces cas, l'effacement de type ne s'applique pas.
Le type générique d'une collection ne devrait avoir d'importance que si elle contient réellement des objets, non? N'est-il pas plus facile de faire simplement:
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
Il n'y a pas de type générique dans l'exécution, mais les objets à l'intérieur lors de l'exécution sont garantis comme étant du même type que le générique déclaré, il est donc assez facile de tester la classe de l'élément avant de le traiter.
Une autre chose que vous pouvez faire est simplement de traiter la liste pour obtenir des membres du bon type, en ignorant les autres (ou en les traitant différemment).
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());
Cela vous donnera une liste de tous les éléments dont les classes étaient des sous-classes Number
dont vous pouvez ensuite traiter selon vos besoins. Les autres éléments ont été filtrés dans d'autres listes. Parce qu'ils sont sur la carte, vous pouvez les traiter comme vous le souhaitez ou les ignorer.
Si vous voulez ignorer complètement les éléments d'autres classes, cela devient beaucoup plus simple:
List<Number> numbers = myCollection.stream()
.filter(Number.class::isInstance)
.map(Number.class::cast)
.collect(Collectors.toList());
Vous pouvez même créer une méthode utilitaire pour vous assurer qu'une liste contient UNIQUEMENT les éléments correspondant à une classe spécifique:
public <V> List<V> getTypeSafeItemList(Collection<Object> input, Class<V> cls) {
return input.stream()
.filter(cls::isInstance)
.map(cls::cast)
.collect(Collectors.toList());
}
Object
s et lorsque vous les retirez de la liste, vous les donniez à un gestionnaire approprié en fonction de leur type.
Pour rechercher le type générique d'un champ:
((Class)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]).getSimpleName()
Si vous avez besoin d'obtenir le type générique d'un type renvoyé, j'ai utilisé cette approche lorsque j'avais besoin de trouver des méthodes dans une classe qui renvoyait un Collection
, puis d'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 produit:
Le type générique est la classe java.lang.String
Développant 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);
}
}
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, ce sera une instance de ParametrizedType
, qui à son tour a des méthodes getRawType()
(qui contiendront List.class
, dans ce cas) et getActualTypeArguments()
, qui renverra un tableau (dans ce cas, de longueur un, contenant soit String.class
ou Integer.class
).
method.getGenericParameterTypes()
pour obtenir les types déclarés des paramètres d'une méthode.
J'ai eu le même problème, mais j'ai plutôt utilisé instanceof. Est-ce ainsi:
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 l'utilisation de transtypages non contrôlés, donc ne faites cela que lorsque vous savez qu'il s'agit d'une liste et de quel type elle peut être.
Généralement impossible, car List<String>
et List<Integer>
partager la même classe d'exécution.
Vous pourrez peut-être réfléchir au type déclaré du champ contenant la liste (si le type déclaré ne fait pas lui-même référence à un paramètre de type dont vous ne connaissez pas la valeur).
Comme d'autres l'ont dit, la seule bonne réponse est non, le type a été effacé.
Si la liste contient un nombre d'éléments différent de zéro, vous pouvez rechercher le type du premier élément (en utilisant sa méthode getClass, par exemple). Cela ne vous dira 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.
List<Integer>
et List<String>
; dans ces cas, l'effacement de type ne s'applique pas.
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();
}
}
getFieldGenericTypefieldGenericType
cela ne peut pas être trouvé
Le type est effacé, vous ne pourrez donc pas. Voir http://en.wikipedia.org/wiki/Type_erasure et http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure
List<Integer>
et List<String>
; dans ces cas, l'effacement de type ne s'applique pas.
Utilisez Reflection pour obtenir le Field
pour ceux-ci, alors vous pouvez simplement faire: field.genericType
pour obtenir le type qui contient également les informations sur les génériques.