Почему использует Набор <Строка> .class недопустимый?

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

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>>

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

36
задан Peter Kriens 18 November 2016 в 09:52
поделиться

3 ответа

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

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

1
ответ дан 27 November 2019 в 06:15
поделиться

Кажется, что в 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 > .class недействителен. Ни стирание, ни границы, похоже, не требуют, чтобы это было недействительным.

0
ответ дан 27 November 2019 в 06:15
поделиться

Универсальные шаблоны инвариантны.

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 Tutorials on Generics , Мелкий шрифт : Общий класс используется всеми его вызовами :

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

 List  l1 = new ArrayList  (); 
Список  l2 = new ArrayList  (); 
System.out.println (l1.getClass () == l2.getClass ()) ; 
 

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

То есть не существует таких вещей, как List .class или List .class ; есть только List.class .

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

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

Обратите внимание на отсутствие каких-либо разрешений для параметров / аргументов универсального типа. Более того,

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

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

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

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

В основном вы не можете использовать обобщения с литералами класса, потому что это просто не имеет смысла: они не реифицируются.

31
ответ дан 27 November 2019 в 06:15
поделиться
Другие вопросы по тегам:

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