web-dev-qa-db-de.com

Rufen Sie den generischen Typ von Java.util.List ab

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?

227
Thys

Wenn es sich tatsächlich um Felder einer bestimmten Klasse handelt, 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 innerhalb des Bereichs der Klasse/Methode befinden, in der Sie etwas darüber wissen müssen, ist es sinnlos, sie zu kennen, da Sie sie bereits selbst deklariert haben.

360
BalusC

Das können Sie 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
17
tsaixingwei

Kurze Antwort: Nein.

Dies ist wahrscheinlich ein Duplikat, es kann momentan kein passendes gefunden werden.

Java verwendet etwas, das als Typlöschung bezeichnet wird, dh zur Laufzeit sind beide Objekte gleichwertig. Der Compiler weiß, dass die Listen Ganzzahlen oder Zeichenfolgen enthalten, und kann somit eine typsichere Umgebung aufrechterhalten. Diese Informationen gehen (auf Objektinstanzbasis) zur Laufzeit verloren, und die Liste enthält nur 'Objekte'.

Sie KÖNNEN etwas über die Klasse herausfinden, durch welche Typen sie parametrisiert werden könnten, aber normalerweise ist dies alles, was "Object" erweitert, d. H. Alles. Wenn Sie einen Typ definieren als

class <A extends MyClass> AClass {....}

AClass.class enthält nur die Tatsache, dass der Parameter A an MyClass gebunden ist, aber darüber hinaus gibt es keine Möglichkeit, dies zu sagen.

17
falstro

Wenn Sie den generischen Typ eines zurückgegebenen Typs abrufen möchten, habe ich diesen Ansatz verwendet, als ich Methoden in einer Klasse suchen musste, die eine Collection zurückgegeben hat, und dann auf ihre generischen Typen zugreifen:

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

9
Aram Kocharyan

Um einen generischen Typ eines Feldes zu finden:

((Class)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]).getSimpleName()
9
Mohsen Kashi

Der generische Typ einer Sammlung sollte nur dann von Bedeutung sein, wenn tatsächlich Objekte darin enthalten sind, oder? Also 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 in Runtime, aber die Objekte, die sich zur Laufzeit befinden, sind mit Sicherheit derselbe Typ wie der deklarierte generische Typ. Daher ist es einfach genug, die Klasse des Elements vor der Verarbeitung zu testen.

7
Steve K

Die Antwort auf Steve K erweitern:

/** 
* 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);
   }
}
6
ggb667

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 Besetzungen. Tun Sie dies also nur, wenn Sie wissen, dass es sich um eine Liste handelt und welche Art es sein kann. 

4
Andy

Zur Laufzeit können Sie nicht.

Über Reflektion sind jedoch die Typparametererreichbar. 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 über die Methoden getRawType() (in diesem Fall List.class) und getActualTypeArguments() verfügt. Diese geben ein Array zurück (in diesem Fall mit der Länge eins, entweder String.class oder Integer.class ).

4
Scott Morrison

Im Allgemeinen nicht möglich, da List<String> und List<Integer> dieselbe Laufzeitklasse gemeinsam nutzen.

Möglicherweise können Sie jedoch über den deklarierten Typ des Felds nachdenken, das die Liste enthält (wenn der deklarierte Typ nicht selbst auf einen Typparameter verweist, dessen Wert Sie nicht kennen).

3
meriton

Wie andere bereits 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. die Methode getClass). Das sagt Ihnen nicht den generischen Typ der Liste, aber es wäre anzunehmen, dass der generische Typ eine übergeordnete Klasse der Typen in der Liste war.

Ich würde den Ansatz nicht befürworten, aber in einer Bindung könnte es nützlich sein.

2
Chris Arguin
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();
    }
}
1
Ashish

Verwenden Sie Reflection, um die Field für diese zu erhalten. Dann können Sie einfach Folgendes tun: field.genericType, um den Typ abzurufen, der die Informationen zu generic enthält.

Der Typ wird gelöscht, sodass Sie dies nicht tun können. Siehe http://en.wikipedia.org/wiki/Type_erasure und http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure

0