Вопрос-ответ

Get generic type of java.util.List

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

У меня есть;

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

Есть ли (простой) способ получить универсальный тип списка?

Переведено автоматически
Ответ 1

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

package com.stackoverflow.q1942644;

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<>();
List<Integer> integerList = new ArrayList<>();

public static void main(String... args) throws Exception {
Class<Test> testClass = Test.class;

Field stringListField = testClass.getDeclaredField("stringList");
ParameterizedType stringListType = (ParameterizedType) stringListField.getGenericType();
Class<?> stringListClass = (Class<?>) stringListType.getActualTypeArguments()[0];
System.out.println(stringListClass); // class java.lang.String

Field integerListField = testClass.getDeclaredField("integerList");
ParameterizedType integerListType = (ParameterizedType) integerListField.getGenericType();
Class<?> integerListClass = (Class<?>) integerListType.getActualTypeArguments()[0];
System.out.println(integerListClass); // class java.lang.Integer
}

}

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

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

Ответ 2

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

Method method = someClass.getDeclaredMethod("someMethod");
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
Ответ 3

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

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

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

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

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

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

Ответ 4

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

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 который вы затем можете обработать по мере необходимости. Остальные элементы были отфильтрованы в другие списки. Поскольку они находятся в map, вы можете обрабатывать их по желанию или игнорировать.

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

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