Msgstr "Установить <? Extends Class <? Extends Throwable >>" на самом деле?
Я использую небольшой общий метод для создания наборов из vararg элементов, например
public <T> Set<T> createSet( T... elements ) { ...
В последнее время, однако, я столкнулся с ситуацией, когда компилятор не делал того, что я ожидал от него. Из следующего createSet() использует только s3 работает.
Set<Class<? extends Throwable>> s1 = createSet( Exception.class, RuntimeException.class );
Set<? extends Class<Throwable>> s2 = createSet( Exception.class, RuntimeException.class );
Set<? extends Class<? extends Throwable>> s3 = createSet( Exception.class, RuntimeException.class );
Может ли кто-нибудь дать ясное объяснение того, почему s3 работает и что может быть неправильным с моим мнением о s1 - что было моим первоначальным кодированием? Спасибо.
Ответы
Ответ 1
Проблема заключается просто в логике вывода. s1
работает просто отлично (ну, кроме предупреждений vararg), если вы явно введите вызов метода:
Set<Class<? extends Throwable>> s1 =
this.<Class<? extends Throwable>>createSet( Exception.class, RuntimeException.class );
Но по умолчанию возвращаемый тип, заданный вашими параметрами, равен Set<Class<? extends Exception>>
(я полагаю, потому что это самая конкретная возможность). Вам просто нужно дать компилятору подсказку здесь, так как без него это по существу пытается сделать это:
Set<Class<? extends Exception>> temp = createSet(Exception.class, RuntimeException.class);
Set<Class<? extends Throwable>> s1 = temp;
Что не допускается, поскольку с точки зрения компилятора вы можете поместить a OutOfMemoryError.class
в temp
, который нарушит его тип.
Изменить
Причина s3
работает для вас, потому что Class<? extends Exception>
можно присваивать Class<? extends Throwable>
:
//this works
Class<? extends Exception> exceptionRef = Exception.class;
Class<? extends Throwable> throwableRef = exceptionRef;
и поэтому ключевое слово extends
предоставляет вам возможность конвертировать из Set<Class<? extends Exception>>
в Set<? extends Class<? extends Throwable>>
:
//this works too
Set<Class<? extends Exception>> exceptionSetRef = ...;
Set<? extends Class<? extends Throwable>> throwableSetRef = exceptionSetRef;
К сожалению, это, вероятно, не то, что вы хотите, с этого момента вы не можете поместить ничего в throwableSetRef
.
Ответ 2
Я думаю, потому что ближайший общий тип Exception и RuntimeException - это Исключение, а не Throwable. T в invokeation createSet() вызывается как Class<? extends Exception>
, и это незаконно назначать Set<Class<? extends Exception>>
переменной типа Set<Class<? extends Throwable>
Это работает:
Set<Class<? extends Exception>> s1 = createSet( Exception.class, RuntimeException.class );
Ответ 3
Чтобы сделать возможные проблемы s1
и s2
более понятными, замените Class<T>
на List<T>
.
Тогда:
List<RuntimeException> runtimeExceptions = new ArrayList<RuntimeException>();
Set<List<RuntimeException>> listsOfRuntimeExceptions =
new HashSet<Lis<RuntimeException>>();
listsOfRuntimeExceptions.add(runtimeExceptions);
Set<? extends List<? extends Throwable>> listsOfThrowables =
listsOfRuntimeExceptions; // This is legal
Set<List<? extends Throwable>> s1 = listsOfThrowables; // Imagine that this is legal
s1.add(Arrays.asList(new Exception())); // Type safety of listsOfRuntimeExceptions is violated
Set<? extends List<Throwable>> s2 = listsOfThrowables; // Imagine that this is legal
s2.get(0).add(new Exception()); // Type safety of runtimeExceptions is violated
Ответ 4
Я думаю, проблема здесь связана с тем, как компилятор определяет "верхнюю границу" (в терминах "Типы" ) для ваших варгаров. Если мы проверим http://download.oracle.com/javase/tutorial/java/generics/non-reifiable-varargs-type.html, мы увидим, что для s1
верхняя граница, которую вы получаете, равна Class<? extends Exception>
(и предупреждение который генерируется). Ошибка в том, что в конце вы пытаетесь преобразовать Set<Class<? extends Exception>>
в Set<Class<? extends Throwable>>
. Если вы переписываете s1 на:
Set<Class<? extends Throwable>> s1 = createSet( Exception.class, RuntimeException.class, Throwable.class);
тогда верхний граничный тип Set<Class<? extends Throwable>>
, снова генерирующий предупреждение, но без ошибок.