Почему использование Collection <String>.class незаконно? - PullRequest
34 голосов
/ 30 апреля 2010

Я озадачен дженериками.Вы можете объявить поле как:

Class<Collection<String>> clazz = ...

Кажется логичным, что вы можете назначить это поле с помощью:

Class<Collection<String>> clazz = Collection<String>.class;

Однако это приводит к ошибке:

Синтаксическая ошибка на токене ">", после этого токена ожидается пустое значение

Таким образом, похоже, что оператор .class не работает с шаблонами.Поэтому я попытался:

  class A<S> { }
  class B extends A<String> { }
  Class<A<String>> c = B.class;

Также не работает, генерирует:

Несоответствие типов: не могу преобразовать из Class<Test.StringCollection> to Class<Collection<String>>

Теперь ядействительно не понимаю, почему это не должно работать.Я знаю, что универсальные типы не реализованы, но в обоих случаях они полностью безопасны, не имея доступа к универсальным типам во время выполнения.У кого-нибудь есть идея?

Ответы [ 3 ]

30 голосов
/ 30 апреля 2010

Обобщения являются инвариантами.

Object o = "someString"; // FINE!
Class<Object> klazz = String.class; // DOESN'T COMPILE!
// cannot convert from Class<String> to Class<Object>

В зависимости от того, что вам нужно, вы можете использовать подстановочные знаки.

Class<? extends Number> klazz = Integer.class; // FINE!

Или, может быть, вам нужно что-то вроде этого:

Class<List<String>> klazz =
   (Class<List<String>>) new ArrayList<String>().getClass();
// WARNING! Type safety: Unchecked cast from
//   Class<capture#1-of ? extends ArrayList> to Class<List<String>>

Что касается нереализованного во время выполнения примера, вы, похоже, хорошо разбираетесь, но в любом случае вот цитата из Обучающих программ по Java по универсальным , Fine Print : Общий класс является общим для всех его призывов :

Что печатает следующий фрагмент кода?

List <String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());

Возможно, вы захотите сказать false, но вы ошибаетесь. Он печатает true, потому что все экземпляры универсального класса имеют один и тот же класс времени выполнения, независимо от их фактических параметров типа.

То есть нет такой вещи как List<String>.class или List<Integer>.class; есть только List.class.

Это также отражено в литералах класса JLS 15.8.2

Литерал класса - это выражение, состоящее из имени класса, интерфейса, массива или типа примитива или псевдотипа void, за которым следуют . и токен class.

Обратите внимание на отсутствие каких-либо допусков для параметров / аргументов универсального типа. Кроме того, * 1 043 *

Ошибка времени компиляции, если происходит любое из следующего:

  • Именованный тип является переменной типа или параметризованным типом, или массивом, тип элемента которого является переменной типа или параметризованным типом.

То есть, это также не компилируется:

void <T> test() {
    Class<?> klazz = T.class; // DOESN'T COMPILE!
    // Illegal class literal for the type parameter T
}

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

1 голос
/ 01 мая 2010

Я согласен с другими ответами и хотел бы объяснить еще один момент:

Объекты Class представляют классы, которые загружаются в память JVM. Каждый объект класса на самом деле является экземпляром .class файла в памяти. Обобщения Java не отдельные классы. Они являются лишь частью механизма проверки типов во время компиляции. Следовательно, они не имеют представления времени выполнения в объекте класса.

0 голосов
/ 01 мая 2010

Кажется, что в Java не хватает литералов классов, нет способа создать литералы классов с общей информацией, хотя в некоторых случаях это может быть полезно. Следовательно, следующий код не может быть вызван, поскольку невозможно предоставить литерал класса

class A<S> {}
<S> A<S> foo( Class<A<S>> clazz ) {}
A<String> a = foo( A<String>.class ) // error

Тем не менее, моя главная проблема заключалась в том, что я не мог назвать его с классом B, расширяющим A. Это было вызвано ограничениями инвариантности. Это было решено с помощью подстановочного знака:

class A<S> {}
class B extends A<String> {}     
<S> A<S> foo( Class<? extends A<S>> clazz ) { return null; }
void test () {
    A<String> s = foo( B.class ); 
}

Тем не менее, я не нашел причину, а основная причина в том, что Class<A<S>>.class недействительно. Кажется, что ни стирание, ни границы не требуют, чтобы это было недействительным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...