Чтобы иметь возможность программно проверять объект List и видеть его универсальный тип. Метод может захотеть вставить объекты на основе универсального типа коллекции. Это возможно в языках, которые реализуют обобщения во время выполнения, а не во время компиляции.
Стив Куо
4
Правильно - единственный способ разрешить обнаружение во время выполнения - это создать подклассы: вы МОЖЕТЕ фактически расширить универсальный тип, а затем использовать рефлексию найти тип поиска, который использовал подтип. Это немножко размышлений, но возможно. К сожалению, нет простого способа обеспечить использование этого подкласса.
StaxMan
1
Конечно, stringList содержит строки и целые числа integerList? Зачем делать это сложнее?
Бен Терли
Ответы:
408
Если это на самом деле поля определенного класса, то вы можете получить их с небольшой помощью отражения:
Вы также можете сделать это для типов параметров и типов возвращаемых методов.
Но если они находятся в той же области действия класса / метода, где вы должны знать о них, то нет смысла их знать, потому что вы уже объявили их сами.
@Oleg Ваша переменная использует <?>(подстановочный тип) вместо <Class>(тип класса). Заменить <?>ВУ <Class>или любой другой <E>.
BalusC
19
Вы можете сделать то же самое для параметров метода:
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
In method.getGenericParameterTypes (); что такое метод?
Нео Рави
18
Краткий ответ: нет.
Это, вероятно, дубликат, не могу найти подходящий прямо сейчас.
Java использует так называемое стирание типов, что означает, что во время выполнения оба объекта эквивалентны. Компилятор знает, что списки содержат целые числа или строки, и поэтому могут поддерживать безопасную среду типов. Эта информация теряется (на основе экземпляра объекта) во время выполнения, и список содержит только «Объекты».
Вы МОЖЕТЕ узнать немного о классе, какими типами он может быть параметризован, но обычно это просто все, что расширяет «Объект», то есть что угодно. Если вы определяете тип как
class<A extendsMyClass>AClass{....}
AClass.class будет содержать только тот факт, что параметр A ограничен MyClass, но более того, невозможно сказать.
Это будет верно, если конкретный класс универсального не указан; в своем примере он явно объявляет списки как List<Integer>и List<String>; в этих случаях стирание типа не применяется.
Haroldo_OK
13
Универсальный тип коллекции должен иметь значение только в том случае, если в ней есть объекты, верно? Так не проще ли просто сделать:
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которые вы можете затем обрабатывать по мере необходимости. Остальные элементы были отфильтрованы в другие списки. Поскольку они находятся на карте, вы можете обрабатывать их по своему усмотрению или игнорировать их.
Если вы хотите полностью игнорировать элементы других классов, это становится намного проще:
А если у вас есть пустая коллекция? Или если у вас есть Collection <Object> с целым числом и строкой?
Falci
Контракт для класса может требовать передачи только списков конечных объектов, и что вызов метода вернет ноль, если список пуст, пуст или если тип списка недопустим.
ggb667
1
В случае пустой коллекции, можно безопасно назначить любому типу списка во время выполнения, потому что в нем ничего нет, и после стирания типа Список - это Список - это Список. Из-за этого я не могу вспомнить ни одного случая, когда будет иметь значение тип пустой коллекции.
Стив К
Ну, это «может иметь значение», если вы удерживаете ссылку в потоке и обрабатываете ее, как только объекты начинают появляться в сценарии с продюсером. Если в списке указаны неправильные типы объектов, могут произойти плохие вещи. Я полагаю, чтобы предотвратить остановку обработки во время сна, пока в списке не останется хотя бы один элемент, прежде чем продолжить.
ggb667
Если у вас есть сценарий типа производитель / потребитель, то планирование списка, имеющего определенный общий тип, не будучи уверенным в том, что может быть добавлено в список, в любом случае является плохим дизайном. Вместо этого вы объявляете его как коллекцию Objects, а когда вы вытаскиваете их из списка, вы передаете их соответствующему обработчику в зависимости от их типа.
Если вам нужно получить универсальный тип возвращаемого типа, я использовал этот подход, когда мне нужно было найти методы в классе, который возвратил a, Collectionи затем получить доступ к их универсальным типам:
/**
* 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:protectedWhateverClass add(List<?> data){//For fluant useageif(data==null)|| data.isEmpty(){thrownewIllegalArgumentException("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);}}
Те же проблемы, что и в ответе Стива К. Что, если список пуст? Что если список имеет тип <Object>, который содержит строку и целое число? Ваш код означает, что вы можете добавлять больше строк только в том случае, если первый элемент в списке является строкой, и вообще не содержит целых чисел ...
subrunner
Если список пуст, вам не повезло. Если в списке есть Объект, и он на самом деле содержит Объект, то вы в порядке, но если в нем есть что-то смешанное, вам не повезло. Стирание означает, что это так хорошо, как вы можете сделать. На мой взгляд, Erasure не хватает. Последствия этого не всегда понятны и недостаточны для решения интересных и желательных случаев использования типичных проблем. Ничто не мешает создать класс, который можно спросить, какой тип он принимает, но это просто не то, как работают встроенные классы. Коллекции должны знать, что они содержат, но увы ...
ggb667
4
Во время выполнения нет, вы не можете.
Однако с помощью отражения параметры типа являются доступными. Пытаться
for(Field field :this.getDeclaredFields()){System.out.println(field.getGenericType())}
Метод getGenericType()возвращает объект Type. В этом случае это будет экземпляр класса ParametrizedType, который, в свою очередь, имеет методы getRawType()(которые List.classв этом случае будут содержать ) и getActualTypeArguments()который будет возвращать массив (в данном случае, длины один, содержащий либо String.classили Integer.class).
У меня была та же проблема, но я использовал instanceof. Сделал это так:
List<Object> listCheck =(List<Object>)(Object) stringList;if(!listCheck.isEmpty()){if(listCheck.get(0)instanceofString){System.out.println("List type is String");}if(listCheck.get(0)instanceofInteger){System.out.println("List type is Integer");}}}
Это включает использование непроверенных приведений, поэтому делайте это только тогда, когда вы знаете, что это список, и какой тип он может быть.
Как правило, невозможно, потому что List<String>и List<Integer>совместно использовать один и тот же класс времени выполнения.
Возможно, вы сможете отразить объявленный тип поля, содержащего список (если объявленный тип сам по себе не ссылается на параметр типа, значение которого вы не знаете).
Как уже говорили другие, единственный правильный ответ - нет, тип был стерт.
Если список содержит ненулевое количество элементов, вы можете исследовать тип первого элемента (например, используя его метод getClass). Это не скажет вам универсальный тип списка, но было бы разумно предположить, что универсальный тип был неким суперклассом типов в списке.
Я бы не защищал такой подход, но в безвыходном положении он может быть полезен.
Это будет верно, если конкретный класс универсального не указан; в своем примере он явно объявляет списки как List<Integer>и List<String>; в этих случаях стирание типа не применяется.
Haroldo_OK
2
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;publicclassGenericTypeOfCollectionTest{publicclassFormBean{}publicclassMyClazz{privateList<FormBean> list =newArrayList<FormBean>();}@Testpublicvoid testName()throwsException{Field[] fields =MyClazz.class.getFields();for(Field field : fields){//1. Check if field is of Collection Typeif(Collection.class.isAssignableFrom(field.getType())){//2. Get Generic type of your fieldClass fieldGenericType = getFieldGenericType(field);//3. Compare with <FromBean>Assert.assertTrue("List<FormBean>",FormBean.class.isAssignableFrom(fieldGenericType));}}}//Returns generic type of any fieldpublicClass 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 & FormBeanreturnnewBoolean(false).getClass();}}
Это будет верно, если конкретный класс универсального не указан; в своем примере он явно объявляет списки как List<Integer>и List<String>; в этих случаях стирание типа не применяется.
Haroldo_OK
0
Используйте Reflection, чтобы получить Fieldих, тогда вы можете просто: field.genericTypeполучить тип, который также содержит информацию об общем.
Ответы:
Если это на самом деле поля определенного класса, то вы можете получить их с небольшой помощью отражения:
Вы также можете сделать это для типов параметров и типов возвращаемых методов.
Но если они находятся в той же области действия класса / метода, где вы должны знать о них, то нет смысла их знать, потому что вы уже объявили их сами.
источник
<?>
(подстановочный тип) вместо<Class>
(тип класса). Заменить<?>
ВУ<Class>
или любой другой<E>
.Вы можете сделать то же самое для параметров метода:
источник
Краткий ответ: нет.
Это, вероятно, дубликат, не могу найти подходящий прямо сейчас.
Java использует так называемое стирание типов, что означает, что во время выполнения оба объекта эквивалентны. Компилятор знает, что списки содержат целые числа или строки, и поэтому могут поддерживать безопасную среду типов. Эта информация теряется (на основе экземпляра объекта) во время выполнения, и список содержит только «Объекты».
Вы МОЖЕТЕ узнать немного о классе, какими типами он может быть параметризован, но обычно это просто все, что расширяет «Объект», то есть что угодно. Если вы определяете тип как
AClass.class будет содержать только тот факт, что параметр A ограничен MyClass, но более того, невозможно сказать.
источник
List<Integer>
иList<String>
; в этих случаях стирание типа не применяется.Универсальный тип коллекции должен иметь значение только в том случае, если в ней есть объекты, верно? Так не проще ли просто сделать:
Универсального типа во время выполнения не существует, но объекты внутри во время выполнения гарантированно совпадают с типом объявленного универсального, поэтому достаточно просто проверить класс элемента перед его обработкой.
Еще одна вещь, которую вы можете сделать, - это просто обработать список, чтобы получить членов правильного типа, игнорируя других (или обрабатывая их по-другому).
Это даст вам список всех элементов, чьи классы были подклассами,
Number
которые вы можете затем обрабатывать по мере необходимости. Остальные элементы были отфильтрованы в другие списки. Поскольку они находятся на карте, вы можете обрабатывать их по своему усмотрению или игнорировать их.Если вы хотите полностью игнорировать элементы других классов, это становится намного проще:
Вы даже можете создать служебный метод, чтобы гарантировать, что список содержит ТОЛЬКО те элементы, которые соответствуют определенному классу:
источник
Object
s, а когда вы вытаскиваете их из списка, вы передаете их соответствующему обработчику в зависимости от их типа.Для нахождения универсального типа одного поля:
источник
Если вам нужно получить универсальный тип возвращаемого типа, я использовал этот подход, когда мне нужно было найти методы в классе, который возвратил a,
Collection
и затем получить доступ к их универсальным типам:Это выводит:
источник
Расширяя ответ Стива К:
источник
Во время выполнения нет, вы не можете.
Однако с помощью отражения параметры типа являются доступными. Пытаться
Метод
getGenericType()
возвращает объект Type. В этом случае это будет экземпляр классаParametrizedType
, который, в свою очередь, имеет методыgetRawType()
(которыеList.class
в этом случае будут содержать ) иgetActualTypeArguments()
который будет возвращать массив (в данном случае, длины один, содержащий либоString.class
илиInteger.class
).источник
method.getGenericParameterTypes()
для получения объявленных типов параметров метода.У меня была та же проблема, но я использовал instanceof. Сделал это так:
Это включает использование непроверенных приведений, поэтому делайте это только тогда, когда вы знаете, что это список, и какой тип он может быть.
источник
Как правило, невозможно, потому что
List<String>
иList<Integer>
совместно использовать один и тот же класс времени выполнения.Возможно, вы сможете отразить объявленный тип поля, содержащего список (если объявленный тип сам по себе не ссылается на параметр типа, значение которого вы не знаете).
источник
Как уже говорили другие, единственный правильный ответ - нет, тип был стерт.
Если список содержит ненулевое количество элементов, вы можете исследовать тип первого элемента (например, используя его метод getClass). Это не скажет вам универсальный тип списка, но было бы разумно предположить, что универсальный тип был неким суперклассом типов в списке.
Я бы не защищал такой подход, но в безвыходном положении он может быть полезен.
источник
List<Integer>
иList<String>
; в этих случаях стирание типа не применяется.источник
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
получить тип, который также содержит информацию об общем.источник