Почему классы java не наследуют аннотации от реализованных интерфейсов?

109

Я использовал AOP Guice для перехвата вызовов некоторых методов. Мой класс реализует интерфейс, и я хотел бы аннотировать методы интерфейса, чтобы Guice мог выбрать правильные методы. Даже если тип аннотации аннотируется классом реализации аннотации Inherited, он не наследует аннотацию, как указано в java-документе Inherited:

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

Что могло быть причиной этого? Ознакомиться со всеми интерфейсами, которые класс объекта реализует во время выполнения, не так уж и сложно, поэтому для этого решения должна быть веская причина.

Борис Павлович
источник

Ответы:

131

Я бы сказал, что причина в том, что в противном случае возникнет проблема множественного наследования.

Пример:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) @Inherited
public @interface Baz { String value(); }

public interface Foo{
    @Baz("baz") void doStuff();
}

public interface Bar{
    @Baz("phleem") void doStuff();
}

public class Flipp{
    @Baz("flopp") public void doStuff(){}
}

public class MyClass extends Flipp implements Foo, Bar{}

Если я сделаю это:

MyClass.class.getMethod("doStuff").getAnnotation(Baz.class).value()

какой будет результат? baz, phleem или flopp?


По этой причине аннотации к интерфейсам редко бывают полезными.

Шон Патрик Флойд
источник
9
аннотации к интерфейсам полезны только в том случае, если у вас есть платформа, которая их поддерживает. Кстати, в этом примере getAnnotation () возвращает null;)
Питер Лоури
6
Не знаю, люди. В этом случае (если бы опечатки не было) я бы ожидал The field value is ambiguous.-подобную ошибку компилятора, как если бы два интерфейса объявляли одну и ту же константу с разными значениями. Я знаю, что это не поле, но все значения аннотации разрешаются во время компиляции, не так ли? Функция, которую нам здесь не хватает, была бы очень полезной во многих случаях. Извините, кстати, что возродил старый пост :).
Петр Янечек
7
@Slanec взгляните на исходники Spring, чтобы увидеть, как ребята из Spring работали над этими проблемами. См. AnnotationUtils.findAnnotation (method, annotationType)
Шон Патрик Флойд
1
Я подумывал написать что-то подобное. С каким-то простым кешированием это кажется мне подходящим вариантом. Спасибо! В приведенном выше примере он найдет Fooаннотацию '( MyClassее нет, тогда интерфейсы ищутся и берутся в том порядке, в котором они следуют implements) и, следовательно, печатает «baz». Прохладно.
Петр Янечек
2
@WChargin, правда, кому-то понадобилось 2 года, чтобы обнаружить эту опечатку :-)
Шон Патрик Флойд
35

Из Javadoc для @Inherited:

Указывает, что тип аннотации наследуется автоматически. Если унаследованная метааннотация присутствует в объявлении типа аннотации, и пользователь запрашивает тип аннотации в объявлении класса, а объявление класса не имеет аннотации для этого типа, то суперкласс класса будет автоматически опрошен на предмет типа аннотации. Этот процесс будет повторяться до тех пор, пока не будет найдена аннотация для этого типа или пока не будет достигнута вершина иерархии классов (объект). Если ни один суперкласс не имеет аннотации для этого типа, то запрос укажет, что рассматриваемый класс не имеет такой аннотации. Обратите внимание, что этот тип метааннотации не действует, если аннотированный тип используется для аннотирования чего-либо, кроме класса. Также обратите внимание, что эта мета-аннотация только вызывает наследование аннотаций от суперклассов;

С другой стороны, валидаторы JSR 305 выполняют своего рода поиск наследования. Если у вас есть иерархия классов:

//Person.java
@Nonnull
 public Integer getAge() {...}

//Student.java (inherits from Person)
@Min(5)
public Integer getAge() {...}

Тогда эффективная валидация на Student.getAge()это @Nonnull @Min(5). @Nonnullне имеет @Inheritedметааннотации.

Эрик Джаблоу
источник
4
Это должен быть выбранный ответ
Анджей Пуртак
3
ваш пример противоречит вашему ответу, в котором говорится, что @Inheritedэто не влияет ни на что, кроме класса
Олег Михеев
@Inherited работает только при использовании аннотации к Class
Карлос Паркер