Получить общий тип java.util.List

У меня есть:

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

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

Ответ 1

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

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

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

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

Ответ 2

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

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

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

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

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

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

Ответ 3

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

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

Ответ 4

Если вам нужно получить общий тип возвращаемого типа, я использовал этот подход, когда мне нужно было найти методы в классе, который возвращал 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.lang.String

Ответ 5

Для нахождения общего типа одного поля:

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

Ответ 6

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

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

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

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

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

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

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

List<Number> numbers = myCollection.stream()
    .filter(Number.class::isInstance)
    .map(Number.class::cast)
    .collect(Collectors.toList());

Вы даже можете создать служебный метод, чтобы гарантировать, что список содержит ТОЛЬКО те элементы, которые соответствуют определенному классу:

public <V> List<V> getTypeSafeItemList(Collection<Object> input, Class<V> cls) {
    return input.stream()
            .filter(cls::isInstance)
            .map(cls::cast)
            .collect(Collectors.toList());
}

Ответ 7

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

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

Ответ 8

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

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

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

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

Ответ 9

Имела ту же проблему, но вместо этого я использовал 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");
       }
    }
}

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

Ответ 10

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

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

Ответ 11

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

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

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

Ответ 12

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

Ответ 14

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