Ich habe;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
Gibt es eine (einfache) Möglichkeit, den generischen Typ der Liste abzurufen?
Ich habe;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
Gibt es eine (einfache) Möglichkeit, den generischen Typ der Liste abzurufen?
Antworten:
Wenn dies tatsächlich Felder einer bestimmten Klasse sind, können Sie sie mit ein wenig Reflexionshilfe erhalten:
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.
}
}
Sie können dies auch für Parametertypen und Rückgabetypen von Methoden tun.
Wenn sie sich jedoch im selben Bereich der Klasse / Methode befinden, in der Sie sie kennen müssen, ist es sinnlos, sie zu kennen, da Sie sie bereits selbst deklariert haben.
<?>
(Platzhaltertyp) anstelle von <Class>
(Klassentyp). Ersetzen Sie Ihre <?>
durch <Class>
oder was auch immer <E>
.
Sie können dasselbe auch für Methodenparameter tun:
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
Kurze Antwort: nein.
Dies ist wahrscheinlich ein Duplikat, kann momentan kein passendes finden.
Java verwendet eine sogenannte Typlöschung, was bedeutet, dass zur Laufzeit beide Objekte gleichwertig sind. Der Compiler weiß, dass die Listen Ganzzahlen oder Zeichenfolgen enthalten, und kann daher eine typsichere Umgebung aufrechterhalten. Diese Informationen gehen zur Laufzeit (auf Objektinstanzbasis) verloren, und die Liste enthält nur 'Objekte'.
Sie können ein wenig über die Klasse herausfinden, von welchen Typen sie möglicherweise parametrisiert wird, aber normalerweise ist dies nur alles, was "Objekt" erweitert, dh alles. Wenn Sie einen Typ definieren als
class <A extends MyClass> AClass {....}
AClass.class enthält nur die Tatsache, dass der Parameter A durch MyClass begrenzt ist, aber darüber hinaus gibt es keine Möglichkeit zu sagen.
List<Integer>
und List<String>
; In diesen Fällen gilt die Typlöschung nicht.
Der generische Typ einer Sammlung sollte nur dann eine Rolle spielen, wenn sie tatsächlich Objekte enthält, oder? Ist es nicht einfacher, einfach zu tun:
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
Es gibt keinen generischen Typ zur Laufzeit, aber die Objekte zur Laufzeit haben garantiert den gleichen Typ wie das deklarierte generische. Es ist also einfach genug, die Klasse des Elements zu testen, bevor wir es verarbeiten.
Sie können die Liste auch einfach verarbeiten, um Mitglieder des richtigen Typs zu erhalten, andere zu ignorieren (oder sie anders zu verarbeiten).
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());
Auf diese Weise erhalten Sie eine Liste aller Elemente, deren Klassen Unterklassen waren, Number
die Sie dann nach Bedarf verarbeiten können. Der Rest der Elemente wurde in andere Listen herausgefiltert. Da sie in der Karte enthalten sind, können Sie sie wie gewünscht verarbeiten oder ignorieren.
Wenn Sie Elemente anderer Klassen vollständig ignorieren möchten, wird dies viel einfacher:
List<Number> numbers = myCollection.stream()
.filter(Number.class::isInstance)
.map(Number.class::cast)
.collect(Collectors.toList());
Sie können sogar eine Dienstprogrammmethode erstellen, um sicherzustellen, dass eine Liste NUR die Elemente enthält, die einer bestimmten Klasse entsprechen:
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 deklarieren und wenn Sie sie aus der Liste ziehen, würden Sie sie einem geeigneten Handler basierend auf ihrem Typ geben.
So finden Sie den generischen Typ eines Felds:
((Class)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]).getSimpleName()
Wenn Sie den generischen Typ eines zurückgegebenen Typs abrufen müssen, habe ich diesen Ansatz verwendet, um Methoden in einer Klasse zu finden, die a zurückgegeben hat, Collection
und dann auf ihre generischen Typen zuzugreifen:
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]);
}
}
}
}
}
}
Dies gibt aus:
Der generische Typ ist die Klasse java.lang.String
Erweitern Sie die Antwort von 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);
}
}
Zur Laufzeit können Sie nicht.
Jedoch durch Reflexion der Typparameter sind zugänglich. Versuchen
for(Field field : this.getDeclaredFields()) {
System.out.println(field.getGenericType())
}
Die Methode getGenericType()
gibt ein Type-Objekt zurück. In diesem Fall handelt es sich um eine Instanz von ParametrizedType
, die wiederum Methoden enthält getRawType()
(die List.class
in diesem Fall enthalten) und getActualTypeArguments()
die ein Array zurückgibt (in diesem Fall von der Länge eins, das entweder String.class
oder enthält Integer.class
).
method.getGenericParameterTypes()
Sie die deklarierten Typen der Parameter einer Methode abrufen .
Hatte das gleiche Problem, aber ich habe stattdessen instanceof verwendet. Habe es so gemacht:
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");
}
}
}
Dies beinhaltet die Verwendung von ungeprüften Casts. Tun Sie dies also nur, wenn Sie wissen, dass es sich um eine Liste handelt und welcher Typ es sein kann.
Im Allgemeinen unmöglich, weil List<String>
und List<Integer>
die gleiche Laufzeitklasse teilen.
Möglicherweise können Sie jedoch über den deklarierten Typ des Felds nachdenken, in dem sich die Liste befindet (wenn der deklarierte Typ selbst nicht auf einen Typparameter verweist, dessen Wert Sie nicht kennen).
Wie andere gesagt haben, ist die einzig richtige Antwort nein, der Typ wurde gelöscht.
Wenn die Liste eine Anzahl von Elementen ungleich Null enthält, können Sie den Typ des ersten Elements untersuchen (z. B. mithilfe der Methode getClass). Das sagt Ihnen nicht den generischen Typ der Liste, aber es wäre vernünftig anzunehmen, dass der generische Typ eine Oberklasse der Typen in der Liste war.
Ich würde den Ansatz nicht befürworten, aber in einer Bindung könnte es nützlich sein.
List<Integer>
und List<String>
; In diesen Fällen gilt die Typlöschung nicht.
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
, kann nicht gefunden werden
Der Typ wird gelöscht, sodass Sie dies nicht können. Siehe http://en.wikipedia.org/wiki/Type_erasure und http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure
List<Integer>
und List<String>
; In diesen Fällen gilt die Typlöschung nicht.
Verwenden Sie Reflection, um die Field
für diese zu erhalten, dann können Sie einfach tun: field.genericType
um den Typ zu erhalten, der auch die Informationen über generische enthält.