Как правильно получить объект класса для класса List без потери информации о типе? - PullRequest
0 голосов
/ 23 января 2020

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

Я хотел бы получить объект класса для класса List. Для не универсального c типа я бы использовал литерал класса, такой как String.class, но для List, хотя я могу получить правильное поведение, я все равно получаю предупреждения о том, что я делаю это неправильно.

Насколько я понимаю, объект класса для List будет иметь тип Class<List>, но даже записывая этот тип,

Class<List> c = null;

выдает предупреждение: «Необработанное использование параметризованного класса« List »». Из-за стирания типа объект класса не может знать об элементах списка, поэтому мое следующее предположение будет

Class<List<?>> c = null;

, которое в качестве типа будет принято. Затем мне нужно получить объект класса, но все попытки записать литерал класса заканчиваются неудачей:

Class<List<?>> l = List.class;

выдает ошибку жесткого типа, в то время как

Class<List<?>> l = List<?>.class;

говорит: «Не могу выберите из параметризованного типа ".

Class<List<?>> l = (Class<List>)List.class;

Сбой с той же ошибкой жесткого типа, что и без преобразования (ожидается, так как приведение является избыточным), а

Class<List<?>> l = (Class<List<?>>)List.class;

говорит" необратимые типы " .

Далее я попробовал следующий недопустимый фрагмент кода, чтобы узнать что-то из ошибки компилятора:

List<?> l = new ArrayList<>();
int x = l.getClass();

Теперь компилятор выдает ошибку типа для Class<? extends java.util.List> - игнорирование расширений "часть на мгновение, что очевидно, так как getClass() может вернуть подкласс, это говорит мне, что .getClass() хочет говорить только о необработанном типе List, а не List<?> или подобном. В некотором смысле это кажется мне правильным, поскольку объект класса представляет любой экземпляр List, а не «списки, тип которых неизвестен». Однако теперь я вернулся к исходной точке, потому что простое записывание необработанного типа дает предупреждение, поэтому я, очевидно, не собираюсь этого делать (я понимаю, что необработанные типы можно использовать для работы с устаревшим кодом, но я не делая этого здесь).

Я понимаю, что мог бы выбросить всю информацию о типе класса stati c об объекте класса следующим образом:

Class<?> c = List.class;

Но это часто неосуществимо, и На самом деле я пытаюсь решить более крупную проблему, где мне нужна эта информация о типах.

Хотя я знаю, что могу легко отключить предупреждения или использовать аналогичные грязные приемы, я хотел бы знать, какой правильный способ получить объект класса для List.

1 Ответ

4 голосов
/ 23 января 2020

Невозможно выиграть эту войну.

Нет такого понятия, как Class<List<?>> или что-то еще. Это допустимый тип , но нет конкретного значения, которое вы можете присвоить этому (кроме null), потому что List.class и new ArrayList<>().getClass() имеют тип Class<List> и Class<? extends List> соответственно.

Экземпляры класса изначально имеют тип raw, из-за стирания типа.

Вы можете делать непроверенные приведения:

Class<List<?>> clazz = (Class<List<?>>) (Class<?>) List.class;

, но это также генерировать предупреждения; и поскольку вы можете выполнять другие небезопасные приведения, такие как:

Class<List<?>> clazz1 = (Class<List<?>>) (Class<?>) ArrayList.class;

, вы можете оказаться в странной ситуации, когда два экземпляра Class<List<?>> не равны, или, например, иногда может быть создан экземпляр Class<List<?>> (используя clazz.newInstance()), а в других случаях нет.


Теперь, если вам нужен какой-то токен универсального типа c, вы можете использовать что-то вроде того, что делает Guice:

TypeLiteral<ArrayList<?>> typeLiteral =
    new TypeLiteral<ArrayList<?>>() {};

можно получить ArrayList<?> из этого во время выполнения из-за способа захвата суперкласса (обратите внимание на {} - это анонимный подкласс TypeLiteral). Вы можете реализовать это самостоятельно довольно легко, если не хотите использовать зависимость от Guice (или других библиотек, предлагающих аналогичные конструкции), используя typeLiteral.getClass().getGenericSuperclass().

Но то, какой подход вы можете использовать, зависит на вашу нераскрытую проблему. Ключевым выводом является то, что дженерики и рефлексия просто не очень хорошо играют вместе.

...