Ответ 1
Причина в том, что Test.class имеет тип Class <Test> . Вы не можете назначить ссылку типа Class <Test> к переменной типа Class <T> поскольку они не одно и то же. Это, однако, работает:
Class<? extends Test> testType = type == null ? Test.class : type;
Подстановочный знак позволяет использовать класс <T> и Class <Test> ссылки, которые будут назначены testType.
Существует тонна информации о поведении Java-дженериков в Часто задаваемые вопросы по Java Generics Angelika Langer. Я приведу пример, основанный на некоторой информации там, где используется базовый API-интерфейс иерархии классов Number
.
Рассмотрим следующий метод:
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
extends Number
, так почему это не удается? Посмотрите на следующие два утверждения, чтобы узнать, почему:
Number anumber = new Long(0);
Integer another = anumber;
Довольно легко понять, почему вторая строка не компилируется здесь. Вы не можете назначить экземпляр 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
, это совершенно законно и потенциально полезно во многих случаях.