Как правило, я видел, что люди используют литерал класса как это:
Class<Foo> cls = Foo.class;
Но что, если тип универсален, например, Список? Это хорошо работает, но имеет предупреждение, так как Список должен быть параметризован:
Class<List> cls = List.class
Итак, почему бы не добавить a <?>
? Ну, это вызывает ошибку несоответствия типов:
Class<List<?>> cls = List.class
Я полагал, что что-то вроде этого будет работать, но это - просто простая синтаксическая ошибка:
Class<List<Foo>> cls = List<Foo>.class
Как я могу получить a Class<List<Foo>>
статически, например, использование литерала класса?
Я мог использовать @SuppressWarnings("unchecked")
избавиться от предупреждений, вызванных непараметризованным использованием Списка в первом примере, Class<List> cls = List.class
, но я быть бы нет.
Какие-либо предложения?
Невозможно из-за стирания типа .
Дженерики Java - это не более чем синтаксический сахар для преобразования объектов. Чтобы продемонстрировать:
List<Integer> list1 = new ArrayList<Integer>();
List<String> list2 = (List<String>)list1;
list2.add("foo"); // perfectly legal
Единственный случай, когда информация об общем типе сохраняется во время выполнения, - это Field.getGenericType ()
при опросе членов класса через отражение.
Вот почему Object.getClass ()
имеет такую сигнатуру:
public final native Class<?> getClass();
Важной частью является Class >
.
Другими словами, из FAQ по Java Generics :
Почему нет литерала класса для конкретных параметризованных типов?
Потому что параметризованный тип не имеет точного представления типа во время выполнения.
Литерал класса обозначает объект
Class
, который представляет данный тип. Например, литерал классаString.class
обозначает объектClass
, который представляет типString
и идентиченClass
объект, который возвращается при вызове методаgetClass
для объектаString
. Литерал класса может использоваться для проверки типа во время выполнения и для отражения.Параметризованные типы теряют свои аргументы типа , когда они переводятся в байтовый код во время компиляции в процессе , называемом стиранием типа. В качестве побочного эффекта стирания типа все экземпляры универсального типа используют одно и то же представление среды выполнения, а именно соответствующее необработанное {{1} } тип . Другими словами, параметризованные типы не имеют собственного представления типа .Следовательно, нет смысла формировать литералы классов , такие как
List
,.class List
и.class List >. Class
, поскольку таких объектовClass
не существует. Только необработанный типList
имеет объектClass
, представляющий его тип среды выполнения . Он называетсяList.class
.
Нет литералов класса для параметризованных типов, однако есть объекты типа, которые правильно определяют эти типы.
См. Java.lang.reflect.ParameterizedType - http://java.sun.com/j2se/1.5.0/docs/api/java/lang/reflect/ParameterizedType.html
Библиотека Google Gson определяет класс TypeToken, который позволяет просто генерировать параметризованные типы и использует его для спецификации объектов json со сложными параметризованными типами в удобном для всех виде. В вашем примере вы должны использовать:
Type typeOfListOfFoo = new TypeToken<List<Foo>>(){}.getType()
Я намеревался разместить ссылки на javadoc классов TypeToken и Gson, но Stack Overflow не позволит мне опубликовать более одной ссылки, так как я новый пользователь, вы можете легко найти их с помощью Google поиск
В развитие ответа cletus'а, во время выполнения все записи о родовых типах удаляются. Дженерики обрабатываются только в компиляторе и используются для обеспечения дополнительной безопасности типов. На самом деле это просто сокращение, которое позволяет компилятору вставлять типовые преобразования в нужных местах. Например, раньше вы должны были сделать следующее:
List x = new ArrayList();
x.add(new SomeClass());
Iterator i = x.iterator();
SomeClass z = (SomeClass) i.next();
становится
List<SomeClass> x = new ArrayList<SomeClass>();
x.add(new SomeClass());
Iterator<SomeClass> i = x.iterator();
SomeClass z = i.next();
Это позволяет компилятору проверить ваш код во время компиляции, но во время выполнения он все равно выглядит как в первом примере.
Из-за того, что литералы Class не имеют информации о родовом типе, я думаю, вы должны предположить, что избавиться от всех предупреждений будет невозможно. В некотором смысле, использование Class
- это то же самое, что использование коллекции без указания общего типа. Лучшее, что я смог придумать:
private <C extends A<C>> List<C> getList(Class<C> cls) {
List<C> res = new ArrayList<C>();
// "snip"... some stuff happening in here, using cls
return res;
}
public <C extends A<C>> List<A<C>> getList() {
return getList(A.class);
}