Несовместимые типы и свежие переменные типа
Я получаю следующее сообщение компиляции:
[javac] ... error: incompatible types
[javac] exceptionClassHolder = new Holder<>( (new Exception()).getClass() );
[javac] ^
[javac] required: Holder<Class<? extends Exception>>
[javac] found: Holder<Class<CAP#1>>
[javac] where CAP#1 is a fresh type-variable:
[javac] CAP#1 extends Exception from capture of ? extends Exception
[javac] 1 error
Мне кажется, что согласно сообщению все должно быть правильно. CAP#1
действительно расширяет исключение. Итак, как следует понимать вышеприведенное сообщение? SSCCE ниже (изначально не опубликовано, так как я надеялся понять само сообщение об ошибке в общем случае):
class Holder<T> {
public T t;
public Holder(T t) {
this.t = t;
}
}
public class FooMain {
public static void main(String args[]) throws Exception {
Holder<Class<? extends Exception>> exceptionClassHolder;
exceptionClassHolder = new Holder<>( (new Exception()).getClass() );
}
}
2 ответа
К сожалению, существующие ответы не объясняют, что здесь происходит. Во-первых, решение состоит в том, чтобы просто указать аргумент типа Holder
:
Holder<Class<? extends Exception>> exceptionClassHolder;
exceptionClassHolder =
new Holder<Class<? extends Exception>>(new Exception().getClass());
Ваша версия не работает потому, что new Exception().getClass()
возвращает Class<? extends Exception>
, где ?
является подстановочным знаком (упоминается в сообщении об ошибке компилятора как CAP#1
). Так как вы используете "алмазный оператор" с new Holder<>
компилятор выводит Class<CAP#1 extends Exception>
за T
так что Holder<Class<CAP#1 extends Exception>>
тип созданного объекта.
Однако это не соответствует вашему объявленному типу Holder<Class<? extends Exception>>
, Он использует вложенный шаблон, который не фиксирует: CAP#1 extends Exception
это какой-то конкретный тип, расширяющий Exception
вложенный ? extends Exception
представляет буквально любой тип, расширяющий Exception
,
И пока Class<CAP#1 extends Exception>
это подтип Class<? extends Exception>
, Holder<Class<CAP#1 extends Exception>>
не является подтипом Holder<Class<? extends Exception>>
потому что генерики не являются ковариантными, поэтому назначение не выполняется.
Указав вручную Class<? extends Exception>
за T
помогите компилятору избежать этой "ловушки".
Смотрите мои похожие ответы на эти сообщения:
Holder<? extends Class<? extends Exception>> exceptionClassHolder;
exceptionClassHolder = new Holder<>( (new Exception()).getClass() );
Причина в том, что
- Потому что вы не используете
Exception.class
но объект исключения, думает Java? extends Exception
необходимо. - То же самое относится и к
getClass()
, снова? extends Class
необходимо, хотя класс является окончательным.
Конечно, однажды это будет упрощено.