Получить универсальный тип java.утиль.Список



У меня есть;



List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();


есть ли (простой) способ получить общий тип списка?

436   14  

14 ответов:

Если это действительно поля определенного класса, то вы можете получить их с небольшой помощью отражения:

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.
    }
}

вы также можете сделать это для типов параметров и тип возвращаемого значения методов.

но если они находятся в той же области класса / метода, где вам нужно знать о них, тогда нет смысла их знать, потому что вы уже объявили их сами.

то же самое можно сделать и для параметров метода:

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

короткий ответ: нет.

Это, вероятно, дубликат, не могу найти подходящий прямо сейчас.

Java использует то, что называется стиранием типа, что означает, что во время выполнения оба объекта эквивалентны. Компилятор знает, что списки содержат целые числа или строки, и поэтому может поддерживать типобезопасную среду. Эта информация теряется (на основе экземпляра объекта) во время выполнения, и список содержит только "объекты".

вы можете узнать немного о классе, какие типы он может быть параметризован, но обычно это просто все, что расширяет "объект", т. е. что угодно. Если вы определяете тип как

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

AClass.класс будет содержать только тот факт, что параметр a ограничен MyClass, но более того, нет никакого способа сказать.

Если вам нужно получить универсальный тип возвращаемого типа, я использовал этот подход, когда мне нужно было найти методы в классе, который возвратил Collection и затем получить доступ к их общим типам:

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]);
                    }
                }
            }
        }

    }

}

вот результаты:

универсальный тип - это класс java.ленг.Строка

общий тип коллекции должен иметь значение только в том случае, если в нем действительно есть объекты, верно? Так не проще ли просто сделать:

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

нет такой вещи, как универсальный тип во время выполнения, но объекты внутри во время выполнения гарантированно будут того же типа, что и объявленный универсальный, поэтому достаточно просто проверить класс элемента перед его обработкой.

для нахождения универсального типа одного поля:

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

расширение ответа Стива к:

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

во время выполнения, нет, вы не можете.

однако через отражение параметры типа are работает. Попробуй

for(Field field : this.getDeclaredFields()) {
    System.out.println(field.getGenericType())
}

метод getGenericType() возвращает объект типа. В этом случае это будет экземпляр ParametrizedType, который в свою очередь имеет методы getRawType() (который будет содержать List.class в данном случае) и getActualTypeArguments(), который вернет массив (в данном случае длины один, содержащий либо String.class или Integer.class).

вообще невозможно, потому что List<String> и List<Integer> совместно использовать один и тот же класс среды выполнения.

вы можете задуматься о объявленном типе поля, содержащего список, хотя (если объявленный тип сам по себе не ссылается на параметр типа, значение которого вы не знаете).

как говорили другие, единственный правильный ответ-Нет, Тип был стерт.

Если список имеет ненулевое число элементов, вы можете исследовать тип первого элемента ( например, используя метод getClass ). Это не скажет вам общий тип списка, но было бы разумно предположить, что общий тип был некоторым суперклассом типов в списке.

Я бы не стал отстаивать этот подход, но в связке это может быть полезно.

была та же проблема, но я использовал instanceof вместо этого. Сделал это так:

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");
       }
    }
}

Это включает в себя использование непроверенных приведений, поэтому делайте это только тогда, когда вы знаете, что это список и какой он может быть.

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(getFieldGenericTypefieldGenericType));
            }
        }
    }

    //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();
    }
}

тип стирается, так что вы не сможете. См.http://en.wikipedia.org/wiki/Type_erasure и http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure

используйте отражение, чтобы получить Field для этих то вы можете просто сделать: field.genericType чтобы получить тип, который содержит информацию о generic, а также.

Comments

    Ничего не найдено.