Pourquoi les classes Java n'héritent pas des annotations des interfaces implémentées?

108

J'ai utilisé l'AOP de Guice pour intercepter certains appels de méthode. Ma classe implémente une interface et j'aimerais annoter les méthodes d'interface afin que Guice puisse sélectionner les bonnes méthodes. Même si le type d'annotation est annoté avec la classe d'implémentation d'annotation Inherited n'hérite pas de l'annotation comme indiqué dans la documentation java d'Inherited:

Notez également que cette méta-annotation entraîne uniquement l'héritage des annotations des superclasses; les annotations sur les interfaces implémentées n'ont aucun effet.

Quelle pourrait être la raison de cela? Connaître toutes les interfaces que la classe d'un objet implémente au moment de l'exécution n'est pas si difficile à faire, il doit donc y avoir une bonne raison derrière cette décision.

Boris Pavlović
la source

Réponses:

130

Je dirais que la raison en est qu'autrement, un problème d'héritage multiple se produirait.

Exemple:

@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{}

Si je fais ceci:

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

quel sera le résultat? «baz», «phleem» ou «flopp»?


Pour cette raison, les annotations sur les interfaces sont rarement utiles.

Sean Patrick Floyd
la source
9
les annotations sur les interfaces ne sont utiles que si vous disposez d'un framework qui les prend en charge. BTW dans cet exemple getAnnotation () retourne null;)
Peter Lawrey
6
Je ne sais pas, les gens. Dans ce cas (si la faute de frappe n'était pas là), je m'attendrais à une The field value is ambiguous.erreur de compilateur semblable à celle de deux interfaces déclarant la même constante avec des valeurs différentes. Je sais que ce n'est pas un champ, mais les valeurs d'annotation sont toutes résolues au moment de la compilation, n'est-ce pas? La fonctionnalité qui nous manque ici serait très utile dans de nombreux cas. Désolé d'avoir relancé un ancien message, au fait :).
Petr Janeček
7
@Slanec examine la façon dont les sources de Spring pour voir comment les gars de Spring ont résolu ces problèmes. Voir AnnotationUtils.findAnnotation (method, annotationType)
Sean Patrick Floyd
1
J'envisageais d'écrire quelque chose de similaire. Avec une sorte de simple mise en cache, cela me semble être une solution. Merci! Dans l'exemple ci-dessus, il trouverait l' Fooannotation de 's ( MyClassn'en a pas, alors les interfaces sont recherchées et prises dans l'ordre dans lequel elles sont après implements) et donc afficher "baz". Cool.
Petr Janeček
2
@WChargin vrai, il a fallu juste 2 ans à quelqu'un pour repérer cette faute de frappe :-)
Sean Patrick Floyd
35

Depuis le Javadoc pour @Inherited:

Indique qu'un type d'annotation est automatiquement hérité. Si une méta-annotation héritée est présente sur une déclaration de type d'annotation et que l'utilisateur interroge le type d'annotation sur une déclaration de classe et que la déclaration de classe n'a pas d'annotation pour ce type, alors la superclasse de la classe sera automatiquement interrogée pour le type d'annotation. Ce processus sera répété jusqu'à ce qu'une annotation pour ce type soit trouvée, ou jusqu'à ce que le haut de la hiérarchie des classes (Object) soit atteint. Si aucune superclasse n'a d'annotation pour ce type, la requête indiquera que la classe en question n'a pas d'annotation de ce type. Notez que ce type de méta-annotation n'a aucun effet si le type annoté est utilisé pour annoter autre chose qu'une classe. Notez également que cette méta-annotation entraîne uniquement l'héritage des annotations des superclasses;

D'autre part, les validateurs JSR 305 effectuent une sorte de recherche d'héritage. Si vous avez une hiérarchie de classes:

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

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

Ensuite, les validations effectives sur Student.getAge()sont @Nonnull @Min(5). @Nonnulln'a pas@Inherited méta-annotation.

Eric Jablow
la source
4
Celui-ci devrait être la réponse choisie
Andrzej Purtak
3
votre exemple contredit votre réponse, qui dit que cela @Inheritedn'a aucun effet sur autre chose que la classe
Oleg Mikheev
@Inherited ne fonctionne que lorsque vous utilisez l'annotation sur Class
Carlos Parker