Pourquoi n'est-il pas possible d'étendre les annotations en Java?

227

Je ne comprends pas pourquoi il n'y a pas d'héritage dans les annotations Java, tout comme les classes Java. Je pense que ce serait très utile.

Par exemple: je veux savoir si une annotation donnée est un validateur. Avec l'héritage, je pourrais naviguer par réflexe dans les superclasses pour savoir si cette annotation étend a ValidatorAnnotation. Sinon, comment puis-je y parvenir?

Alors, quelqu'un peut-il me donner une raison pour cette décision de conception?

sinuhepop
la source
2
Notez, BTW, que toutes les annotations s'étendent java.lang.annotation.Annotation, c'est-à-dire que toute annotation l'est instanceofbien que ce fait ne soit pas explicitement déclaré.
Tomáš Záluský

Réponses:

166

À propos de la raison pour laquelle il n'a pas été conçu de cette façon, vous pouvez trouver la réponse dans la FAQ de conception JSR 175 , où il est dit:

Pourquoi ne prenez-vous pas en charge le sous-typage d'annotation (lorsqu'un type d'annotation en étend un autre)?

Cela complique le système de type d'annotation et rend beaucoup plus difficile l'écriture d '«outils spécifiques».

«Outils spécifiques» - Programmes qui interrogent les types d'annotation connus de programmes externes arbitraires. Les générateurs de talons, par exemple, entrent dans cette catégorie. Ces programmes liront les classes annotées sans les charger dans la machine virtuelle, mais chargeront les interfaces d'annotation.

Donc, oui, je suppose, la raison en est juste KISS. Quoi qu'il en soit, il semble que ce problème (ainsi que de nombreux autres) soit examiné dans le cadre de JSR 308 , et vous pouvez même trouver un compilateur alternatif avec cette fonctionnalité déjà développée par Mathias Ricken .

pedromarce
la source
67
Eh bien, je suis peut-être stupide, mais je pense que c'est dommage de ne pas pouvoir prolonger les annotations juste pour "garder les choses simples". Au moins, les concepteurs Java ne pensaient pas la même chose de l'héritage de classe: P
sinuhepop
2
Java 8 M7, ne semble pas prendre en charge les annotations de sous-classement. Quel dommage.
Ceki
2
@assylias JEP 104 ne vise pas à permettre de sous-classer des annotations. JEP 104 a été implémenté dans Java 8, mais il n'est toujours pas possible de sous-classer les annotations (faire une annotation étendre une autre annotation).
Jesper
71

Les annotations extensibles ajouteraient effectivement la charge de spécifier et de maintenir un autre type de système. Et ce serait un système de type assez unique, vous ne pourriez donc pas simplement appliquer un paradigme de type OO.

Réfléchissez à tous les problèmes lorsque vous introduisez le polymorphisme et l'héritage dans une annotation (par exemple, que se passe-t-il lorsque la sous-annotation modifie les spécifications de méta-annotation telles que la rétention?)

Et toute cette complexité supplémentaire pour quel cas d'utilisation?

Vous voulez savoir si une annotation donnée appartient à une catégorie?

Essaye ça:

@Target(ElementType.ANNOTATION_TYPE)
public @interface Category {
    String category();
}

@Category(category="validator")
public @interface MyFooBarValidator {

}

Comme vous pouvez le voir, vous pouvez facilement regrouper et classer les annotations sans douleur excessive en utilisant les fonctionnalités fournies.

Ainsi, KISS est la raison pour ne pas introduire un système de type méta-type dans le langage Java.

[ps modifier]

J'ai utilisé la chaîne simplement pour la démonstration et en vue d'une méta annotation ouverte. Pour votre propre projet, vous pouvez évidemment utiliser une énumération de types de catégories et spécifier plusieurs catégories ("héritage multiple") pour une annotation donnée. Notez que les valeurs sont entièrement fausses et à des fins de démonstration uniquement:

@Target(ElementType.ANNOTATION_TYPE)
public @interface Category {
    AnnotationCategory[] category();
}
public enum AnnotationCategory {
    GENERAL,
    SEMANTICS,
    VALIDATION,
    ETC
}

@Category(category={AnnotationCategory.GENERAL, AnnotationCategory.SEMANTICS})
public @interface FooBarAnnotation {

}

alphazero
la source
Ne fonctionne pas affichera faux:@Target(ElementType.ANNOTATION_TYPE) @Retention(RetentionPolicy.RUNTIME) @interface C {}; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @C public @interface F {} class a{ @F public void S() {} } @Test public void blahTest() throws NoSuchMethodException { Method m = a.class.getMethod("S"); System.out.println(m.isAnnotationPresent(C.class)); }
user1615664
Nous annotons une annotation. a # S a l'annotation F. F lui-même est annoté par C. Essayez cela.
alphazero
Oui c'est correct. Mais existe-t-il un moyen de le faire plus proprement plutôt que de lire le proxy d'annotation?
user1615664
12

Dans un sens, vous l'avez déjà avec Annotations - meta Annotations. Si vous annotez une annotation avec des métadonnées, cela équivaut à bien des égards à l'extension d'une interface supplémentaire. Les annotations sont des interfaces, donc le polymorphisme n'entre pas vraiment en jeu, et comme elles sont de nature statique, il ne peut y avoir de répartition dynamique à l'exécution.

Dans votre exemple de validateur, vous pouvez simplement sur l'annotation obtenir le type annoté et voir s'il a une méta-annotation de validateur.

Le seul cas d'utilisation que j'ai pu voir que l'héritage serait utile si vous vouliez pouvoir obtenir l'annotation par super type, mais cela ajouterait beaucoup de complexité, car une méthode ou un type donné peut avoir deux annotations de ce type, ce qui signifie qu'un tableau devrait être retourné au lieu d'un seul objet.

Je pense donc que la réponse ultime est que les cas d'utilisation sont ésotériques et compliquent des cas d'utilisation plus standard, ce qui ne vaut pas la peine.

Yishai
la source
5

Les concepteurs de la prise en charge des annotations Java ont effectué un certain nombre de «simplifications» au détriment de la communauté Java.

  1. Aucun sous-type d'annotations ne rend la plupart des annotations complexes inutilement laides. On ne peut pas simplement avoir un attribut dans une annotation qui peut contenir une des trois choses. Il faut avoir trois attributs distincts, ce qui déroute les développeurs et nécessite une validation d'exécution pour garantir qu'un seul des trois est utilisé.

  2. Une seule annotation d'un type donné par site. Cela a conduit au modèle d'annotation de collection complètement inutile. @Validation et @Validations, @Image et @Images, etc.

Le second est corrigé dans Java 8, mais il est trop tard. De nombreux frameworks ont été écrits sur la base de ce qui était possible dans Java 5 et maintenant, ces verrues API sont là pour rester longtemps.

Konstantin Komissarchik
la source
1
Non seulement cela, il rend également impossible de définir des propriétés qui ont des attributs imbriqués récursivement - qui est pris en charge par le schéma XML. Cela rend les annotations strictement moins puissantes que XML
user2833557
3

J'ai peut-être trois ans de retard pour répondre à cette question, mais je l'ai trouvée intéressante parce que je me suis retrouvée au même endroit. Voici mon point de vue là-dessus. Vous pouvez afficher les annotations sous forme d'énumérations. Ils fournissent un type d'information à sens unique - utilisez-le ou perdez-le.

J'ai eu une situation où je voulais simuler GET, POST, PUT et DELETE dans une application Web. Je voulais tellement avoir une "super" annotation qui s'appelait "HTTP_METHOD". Il m'est apparu plus tard que cela n'avait pas d'importance. Eh bien, j'ai dû me contenter d'utiliser un champ caché dans le formulaire HTML pour identifier DELETE et PUT (car POST et GET étaient disponibles de toute façon).

Côté serveur, j'ai recherché un paramètre de requête caché avec le nom "_method". Si la valeur était PUT ou DELETE, elle a remplacé la méthode de requête HTTP associée. Cela dit, peu importait que j'aie ou non besoin d'étendre une annotation pour faire le travail. Toutes les annotations se ressemblaient, mais elles étaient traitées différemment côté serveur.

Donc, dans votre cas, supprimez la démangeaison pour étendre les annotations. Traitez-les comme des «marqueurs». Ils "représentent" certaines informations et ne "manipulent" pas nécessairement certaines informations.

mainas
la source
2

Une chose à laquelle je pourrais penser est la possibilité d'avoir plusieurs annotations. Vous pouvez donc ajouter un validateur et une annotation plus spécifique au même endroit. Mais je peux me tromper :)

Janusz
la source
2

Je n'y ai jamais pensé mais ... semble que vous avez raison, il n'y a aucun problème avec la fonction d'héritage des annotations (au moins je ne vois pas le problème avec ça).

À propos de votre exemple avec l' annotation «validateur» - vous pouvez alors exploiter l' approche «méta-annotation» . C'est-à-dire que vous appliquez une méta-annotation particulière à l'ensemble de l'interface d'annotation.

denis.zhdanov
la source
J'ai peut-être trois ans de retard pour répondre à cette question, mais je l'ai trouvée intéressante parce que je me suis retrouvée au même endroit.
mainas
1

le même problème que j'ai. Non, tu ne peux pas. Je me suis «discipliné» pour écrire des propriétés dans des annotations afin de respecter certaines normes, donc à l'extérieur lorsque vous obtenez une annotation, vous pouvez «renifler» le type d'annotation qu'il s'agit en fonction des propriétés qu'il possède.

ante.sabo
la source