Java: как я получаю литерал класса от универсального типа?

Как правило, я видел, что люди используют литерал класса как это:

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, но я быть бы нет.

Какие-либо предложения?

184
задан moffeltje 2 March 2018 в 05:54
поделиться

4 ответа

Невозможно из-за стирания типа .

Дженерики 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 .

154
ответ дан 23 November 2019 в 05:58
поделиться

Нет литералов класса для параметризованных типов, однако есть объекты типа, которые правильно определяют эти типы.

См. 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 поиск

57
ответ дан 23 November 2019 в 05:58
поделиться

В развитие ответа 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();

Это позволяет компилятору проверить ваш код во время компиляции, но во время выполнения он все равно выглядит как в первом примере.

6
ответ дан 23 November 2019 в 05:58
поделиться

Из-за того, что литералы 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);
}
1
ответ дан 23 November 2019 в 05:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: