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

Java: how do I get a class literal from a generic type?

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

Обычно я видел, как люди используют литерал класса следующим образом:

Class<Foo> cls = Foo.class;

Но что, если тип является универсальным, например List? Это работает нормально, но выдает предупреждение, поскольку List должен быть параметризован:

Class<List> cls = List.class

Так почему бы не добавить <?>? Ну, это вызывает ошибку несоответствия типов:

Class<List<?>> cls = List.class

Я полагал, что что-то подобное сработает, но это всего лишь обычная синтаксическая ошибка:

Class<List<Foo>> cls = List<Foo>.class

Как я могу получить Class<List<Foo>> статически, например, используя литерал класса?

Я мог бы использовать @SuppressWarnings("unchecked"), чтобы избавиться от предупреждений, вызванных непараметризованным использованием List в первом примере, Class<List> cls = List.class но я бы предпочел этого не делать.

Есть предложения?

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

Вы не можете из-за удаления типа.

Дженерики Java - это немного больше, чем синтаксический сахар для приведения объектов. Чтобы продемонстрировать:

List<Integer> list1 = new ArrayList<Integer>();
List<String> list2 = (List<String>)list1;
list2.add("foo"); // perfectly legal

Единственный экземпляр, где информация об универсальном типе сохраняется во время выполнения, - это with Field.getGenericType() при запросе членов класса с помощью отражения.

Вот почему все это Object.getClass() имеет такую подпись:

public final native Class<?> getClass();

Важная часть заключается в том, что Class<?>.

Другими словами, из часто задаваемых вопросов по Java Generics:


Почему нет литерала класса для конкретных параметризованных типов?


Потому что параметризованный тип не имеет точного представления типа среды выполнения.


Литерал класса обозначает Class объект, представляющий данный тип. Например, литерал класса String.class обозначает Class объект, представляющий тип String и идентичен Class объекту, который возвращается при вызове метода getClass для String объекта. Литерал класса можно использовать для проверки типов во время выполнения и для отражения.


Параметризованные типы теряют свои аргументы типа , когда они преобразуются в байтовый код во время компиляции в процессе , называемом стиранием типа . В качестве побочного эффекта удаления типа все экземпляры универсального типа используют одно и то же представление во время выполнения, а именно представление соответствующего необработанного типа . Другими словами, параметризованные типы не имеют собственного представления типа. Следовательно, нет смысла формировать литералы класса, такие как List<String>.class , List<Long>.class и List<?>.class , поскольку таких Class объектов не существует. Только необработанный тип List имеет Class объект, который представляет его тип среды выполнения. Он называется List.class.


Ответ 2

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

Смотрите 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()

Я намеревался разместить ссылки на классы TypeToken и Gson javadoc, но Stack Overflow не позволяет мне размещать более одной ссылки, поскольку я новый пользователь, вы можете легко найти их с помощью поиска Google

Ответ 3

Вы можете управлять им с помощью двойного приведения :

@SuppressWarnings("unchecked")
Class<List<Foo>> cls = (Class<List<Foo>>)(Object)List.class

Ответ 4

Чтобы пояснить ответ клетуса, во время выполнения все записи универсальных типов удаляются. Универсальные типы обрабатываются только в компиляторе и используются для обеспечения дополнительной безопасности типов. На самом деле это просто сокращение, которое позволяет компилятору вставлять приведения типов в соответствующие места. Например, ранее вам нужно было выполнить следующее:

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();

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

java generics class