Причина в том, что Test.class имеет тип Class . Вы не можете присвоить ссылку типа Class переменной типа Class , так как они не одно и то же. Это, однако, работает:
Class<? extends Test> testType = type == null ? Test.class : type;
Подстановочный знак позволяет назначать ссылки на Class и Class на testType.
Тонну информации о поведении обобщенных Java-программ можно найти по адресу Angelika Langer FAQ по обобщенным Java-компонентам . Я приведу пример, основанный на некоторой информации, которая использует базовый API Java класса Number
heirarchy.
Рассмотрим следующий метод:
public <T extends Number> void testNumber(final Class<T> type)
Это позволяет успешно компилировать следующие операторы:
testNumber(Integer.class);
testNumber(Number.class);
Но следующее не скомпилируется:
testNumber(String.class);
Теперь рассмотрим следующие утверждения:
Class<Number> numberClass = Number.class;
Class<Integer> integerClass = numberClass;
Вторая строка не компилируется и выдает эту ошибку Type mismatch: cannot convert from Class<Number> to Class<Integer>
. Но Integer
расширяет Number
, так почему же он терпит неудачу? Посмотрите на следующие два утверждения, чтобы понять почему:
Number anumber = new Long(0);
Integer another = anumber;
Довольно легко понять, почему 2-я строка здесь не компилируется. Вы не можете присвоить экземпляр Number
переменной типа Integer
, потому что нет способа гарантировать, что экземпляр Number
имеет совместимый тип. В этом примере Number
- это на самом деле Long
, который, безусловно, не может быть присвоен Integer
. На самом деле ошибка также является несовпадением типов: Type mismatch: cannot convert from Number to Integer
.
Правило состоит в том, что экземпляр не может быть назначен переменной, которая является подклассом типа экземпляра, поскольку нет гарантии, что он совместим.
Дженерики ведут себя аналогичным образом. В сигнатуре обобщенного метода T
- это просто заполнитель, указывающий, что метод позволяет компилятору. Когда компилятор встречает testNumber(Integer.class)
, он по существу заменяет T
на Integer
.
Подстановочные знаки добавляют дополнительную гибкость, так как компилируется следующее:
Class<? extends Number> wildcard = numberClass;
Поскольку Class<? extends Number>
обозначает любой тип, который является Number
или подклассом Number
, это совершенно законно и потенциально полезно во многих обстоятельствах.