У мене є;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
Чи є (простий) спосіб отримати загальний тип списку?
У мене є;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
Чи є (простий) спосіб отримати загальний тип списку?
Відповіді:
Якщо це насправді поля певного класу, ви можете отримати їх за допомогою невеликої допомоги:
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.
}
}
Ви також можете це зробити для типів параметрів та методів повернення.
Але якщо вони знаходяться в тій самій області класу / методу, де вам потрібно знати про них, тоді їх немає сенсу знати, тому що ви вже заявили їх самі.
<?>
(wildcard) замість <Class>
(class class). Замініть свою <?>
на <Class>
будь-яку іншу <E>
.
Ви можете зробити те ж саме для параметрів методу:
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.class буде містити лише той факт, що параметр A обмежений MyClass, але більше того, це неможливо сказати.
List<Integer>
і List<String>
; у цих випадках стирання типу не застосовується.
Загальний тип колекції повинен мати значення лише в тому випадку, якщо в ньому насправді є об’єкти, правда? Так чи не простіше просто зробити:
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());
}
Object
s, і коли ви витягнете їх зі списку, ви надасте їх відповідному обробнику залежно від їх типу.
Для знаходження загального типу одного поля:
((Class)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]).getSimpleName()
Якщо вам потрібно отримати загальний тип повернутого типу, я використовував цей підхід, коли мені потрібно було знайти методи в класі, який повернув a, 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
Розгортання відповіді Стіва К:
/**
* 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);
}
}
Ні, ви не можете.
Однак з допомогою відображення параметри типу є доступними. Спробуйте
for(Field field : this.getDeclaredFields()) {
System.out.println(field.getGenericType())
}
Метод getGenericType()
повертає об'єкт Type. У цьому випадку це буде екземпляр ParametrizedType
, який, у свою чергу, має методи getRawType()
(які будуть містити List.class
в даному випадку) і getActualTypeArguments()
який поверне масив (у цьому випадку довжиною один, що містить або String.class
або Integer.class
).
method.getGenericParameterTypes()
для отримання оголошених типів параметрів методу.
Була така ж проблема, але я використовував 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");
}
}
}
Це передбачає використання неперевірених ролях, тому робіть це лише тоді, коли ви знаєте, що це список, і який він може бути.
Як казали інші, єдино правильна відповідь - ні, тип був стертий.
Якщо у списку є ненульова кількість елементів, ви можете дослідити тип першого елемента (наприклад, використовуючи метод getClass). Це не скаже вам загальний тип списку, але було б розумно припустити, що родовий тип був деяким надкласом типів у списку.
Я б не виступав за такий підхід, але в ув'язуванні він може бути корисним.
List<Integer>
і List<String>
; у цих випадках стирання типу не застосовується.
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
це неможливо знайти
Тип стирається, тому ви не зможете. Дивіться http://en.wikipedia.org/wiki/Type_erasure та http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure
List<Integer>
і List<String>
; у цих випадках стирання типу не застосовується.
Використовуйте Reflection, щоб отримати Field
ці, тоді ви можете просто зробити: field.genericType
щоб отримати тип, який містить інформацію про загальне.