2015-07-04 3 views
5

Проблема заключается в общем ограничении:Возможно ли реализовать метод с подписью List <Class <? расширяет аннотацию >> в Java?

public List<Class<? extends Annotation>> getAnnotations() { 
    return new ArrayList<>(Arrays.asList(Override.class)); 
} 

Реальный возвращаемый тип ArrayList<Class<Override>>
Метод ожидает List<Class<? extends Annotation>>

Class<Override> является подтипом Class<? extends Annotation>
Class<? extends Annotation> c = Override.class; // разрешено

ArrayList является подтип List, если типы элементов соответствуют:
List<? extends Number> l = new ArrayList<Integer>(); // разрешено

Однако это не допускается:

List<Class<? extends Annotation>> l = Arrays.asList(Override.class); 
List<Class<? extends Annotation>> l = new ArrayList<>(Arrays.asList(Override.class)); 

Возможно ли это или Class шаблоны разбиты?

+0

@kocko какой java вы использовали? У меня есть jdk1.7_079, вот скриншот https://www.dropbox.com/s/p6gybp1jct19ehg/generics.jpg – AdamSkywalker

+1

Я немного поиграл с этим, и я думаю, что нашел причину. Через некоторое время я напишу ответ. –

+1

От тестирования, похоже, в Java 7 вам необходимо параметризовать статический вызов 'Arrays. > asList (Override.class); '. С Java 8 мне не нужно было –

ответ

2

Я бы предположил, что это из-за характера вывода типа jdk 1.7.

Как вы уже знаете, метод Arrays.asList(T ... elems) универсальна, но мы редко явно указать тип-параметр, который мы хотели бы метод работать и, таким образом, мы полагаться на тип логического вывода функции компилятора.

Итак, когда компилятор видит в Arrays.asList(Override.class) заявлении будет вывод, что тип-параметр для метода следует заменить Class<Override>, то есть мы бы версии метода в следующем виде:

public List<Class<Override>> asList(Class<Override> ... elems) 

Однако, если вы явно установить параметр типа для метода

List<Class<? extends Annotation>> l = 
       Arrays.<Class<? extends Annotation>>asList(Override.class); 

, то компилятор будет на самом деле знает, что тип-параметр должен быть заменен, а затем версия метода .asList() будет:

public List<? extends Annotation> asList(Class<? extends Annotation> ... elems) 

Теперь это будет компилироваться, так как Class<? extends Annotation> совместим до Class<Override>. В Java8 функция вывода типов улучшается еще больше, так что вам не нужно явно устанавливать параметр типа для метода .asList().

Однако более интересный вопрос идет в

Почему List<Class<Override>> не совместим с List<Class<? extends Annotation>>?

The java.lang.Class является final один, который помог бы ответить на следующие два вопроса, сочетание которых будет отвечать на поставленный выше вопрос.:)

Так,

  • Что делает List<Class<Override>> значит?

List<Class<Override>> означает, что мы можем добавлять только экземпляры Class<Override> и ничего больше, кроме этого. Это здорово, зная, что мы не можем даже добавить подклассы Class<Override>, так как тип Class - это final.

  • Что означает List<Class<? extends Annotation>>?

Этого типа List представляет собой целую семьи списков классов, все из которых являются подклассами типа Annotation, что означает, что мы можем успешно добавить любой тип аннотации (например, SuppressWarnings.class, Override.class, Documented.class и т. д.) в список.

Давайте предположим, что следующий пример был на самом деле правильно:

List<Class<Override>> overrides = Arrays.asList(Override.class); 
List<Class<? extends Annotation>> annotations = new ArrayList<>(); 
annotations = overrides; 
annotations.add(SuppressWarnings.class); //HUGE PROBLEM 
annotations.add(Documented.class); //ANOTHER HUGE PROBLEM 

Две огромные проблемы возникают из-за того, что мы пытаемся добавить Непро- Override экземпляры в overrides, что очень неправильно.

У нас есть достаточно умный компилятор, который может фактически обнаружить такие возможные проблемы, и бросать ошибку времени компиляции - это способ помешать нам сделать это.

Подробнее:

+0

спасибо за потраченное время. первая часть говорит, что мы просто обманули детектирование типа. насколько я знаю, в Java8 есть изменения, которые анализируют левую часть оператора присваивания, поэтому он решает эту проблему. вторая часть относится к классической проблеме подтипов в коллекциях, я знаком с этим. давайте подождем несколько дней, чтобы увидеть, пропустили ли мы что-то здесь, и если все будет в порядке, я приму это. Еще раз спасибо :) – AdamSkywalker

+0

Добро пожаловать. :) –

1

ArrayList является подтипом списка, если типы элементов совпадают:

List<? extends Number> l = new ArrayList<Integer>(); // allowed 

Да, но в вашем примере типы элементов не совпадают:

List<Class<? extends Annotation>> l = new ArrayList<Class<Override>>(); 

Конечно, Class<Override> является подтипом Class<? extends Annotation>, но так же, как List<String> не является подтипом List<Object>, List<Class<Override>> не является подтипом List<Class<? extends Annotation>>. Однако это будет подтип List<? extends Class<? extends Annotation>>.

Таким образом, причина, по которой ваш код не скомпилирован, заключается в том, что в Java 7 ввод типа не учитывает возвращаемый тип метода при выводе выражения выражения оператора return, поэтому по умолчанию используется самый конкретный тип, могут быть отнесены к

Arrays.asList(Override.class) 

не понимая, что оператор возврата будет составлять только с более гибким типом (Java умозаключение 8 типа умнее, кстати).Один из способов заключается в явной форме указать аргумент типа:

Arrays.<Class<? extends Annotation>(Override.class); 

или дать вывод типа Java 7 в подсказку, присвоив локальной переменной:

List<Class<? extends Annotation>> list = Arrays.asList(Override.class); 
return list; 

или изменить тип метода возврата к

List<? extends Class<? extends Annotation>> getAnnotations() 

поэтому выведенный тип не имеет значения.

+0

последний код строки выглядит смешно, но работает, lol – AdamSkywalker

Смежные вопросы