web-dev-qa-db-fra.com

Plusieurs annotations du même type sur un élément?

Je tente de slap deux ou plusieurs annotations du même type sur un seul élément, dans ce cas, une méthode. Voici le code approximatif avec lequel je travaille:

public class Dupe {
    public @interface Foo {
      String bar();
    }

    @Foo(bar="one")
    @Foo(bar="two")
    public void haha() {}
}

Lors de la compilation de ce qui précède, javac se plaint d'une annotation en double:

 max @ upsight: ~/work/daybreak $ javac Dupe.Java 
 Dupe.Java:5: annotation en double 

N’est-il tout simplement pas possible de répéter des annotations de la sorte? Pédantiquement, les deux instances de @Foo ci-dessus ne sont-elles pas différentes en raison de la différence de leur contenu?

Si ce qui précède n'est pas possible, quelles sont les solutions de contournement possibles?

UPDATE: On m'a demandé de décrire mon cas d'utilisation. Voici.

Je suis en train de construire un mécanisme très sucré dans la syntaxe pour "mapper" les POJO dans des magasins de documentation tels que MongoDB. Je veux permettre aux index d'être spécifiés comme des annotations sur les accesseurs ou les setters. Voici un exemple artificiel:

public class Employee {
    private List<Project> projects;

    @Index(expr = "project.client_id")
    @Index(expr = "project.start_date")
    public List<Project> getProjects() { return projects; }
}

Évidemment, je veux pouvoir trouver rapidement des instances de Employee par différentes propriétés de Project. Je peux soit spécifier deux fois @ Index avec différentes valeurs expr (), soit adopter l'approche spécifiée dans la réponse acceptée. Même si Hibernate fait cela et que ce n'est pas considéré comme un hack, je pense qu'il est toujours logique d'autoriser au moins la possibilité d'avoir plusieurs annotations du même type sur un seul élément.

81
Max A.

Deux annotations ou plus du même type ne sont pas autorisées. Cependant, vous pourriez faire quelque chose comme ça:

public @interface Foos {
    Foo[] value();
}

@Foos({@Foo(bar="one"), @Foo(bar="two")})
public void haha() {}

Vous aurez cependant besoin d'une gestion dédiée de l'annotation Foos dans le code.

au fait, je viens de l'utiliser il y a 2 heures pour résoudre le même problème

131
sfussenegger

Dans Java 8 (publié en mars 2014), il est possible d'écrire des annotations répétées/en double. Voir http://docs.Oracle.com/javase/tutorial/Java/annotations/repeating.html .

57
mernst

http://docs.Oracle.com/javase/tutorial/Java/annotations/repeating.html

A partir de Java8, vous pouvez décrire des annotations répétables:

@Repeatable(FooValues.class)
public @interface Foo {
    String bar();
}

public @interface FooValues {
    Foo[] value();
}

Remarque: value est un champ obligatoire pour la liste de valeurs.

Maintenant, vous pouvez utiliser des annotations pour les répéter au lieu de remplir le tableau:

@Foo(bar="one")
@Foo(bar="two")
public void haha() {}
14
Sergei Pikalev

En plus des autres moyens mentionnés, il existe un moyen plus détaillé en Java8:

@Target(ElementType.TYPE)
@Repeatable(FooContainer.class)
@Retention(RetentionPolicy.RUNTIME)
@interface Foo {
    String value();

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface FooContainer {
        Foo[] value();
        }

@Foo("1") @Foo("2") @Foo("3")
class Example{

}

Exemple par défaut, FooContainer sous forme d'annotation

    Arrays.stream(Example.class.getDeclaredAnnotations()).forEach(System.out::println);
    System.out.println(Example.class.getAnnotation(FooContainer.class));

Les deux imprimés ci-dessus:

@ com.FooContainer (valeur = [@ com.Foo (valeur = 1), @ com.Foo (valeur = 2), @ com.Foo (valeur = 3)]) 

@ com.FooContainer (valeur = [@ com.Foo (valeur = 1), @ com.Foo (valeur = 2), @ com.Foo (valeur = 3)])

12
Jatin

Comme l'a dit Sfussenegger, ce n'est pas possible.

La solution habituelle consiste à construire une annotation "multiple" , qui gère un tableau de l'annotation précédente. Il est généralement nommé le même, avec un suffixe 's'.

À propos, ceci est très utilisé dans les grands projets publics (Hibernate par exemple), il ne devrait donc pas être considéré comme un piratage, mais plutôt comme une solution correcte pour ce besoin.


Selon vos besoins, il pourrait être préférable de permettre à votre annotation précédente de gérer plusieurs valeurs .

Exemple:

    public @interface Foo {
      String[] bars();
    }
12
KLE

combinant les autres réponses dans la forme la plus simple ... une annotation avec une simple liste de valeurs ...

@Foos({"one","two"})
private String awk;

//...

public @interface Foos{
    String[] value();
}
4
matt1616

Si vous n'avez qu'un seul paramètre "bar", vous pouvez le nommer "valeur". Dans ce cas, il ne sera pas nécessaire d'écrire le nom du paramètre lorsque vous l'utiliserez comme ceci:

@Foos({@Foo("one"), @Foo("two")})
public void haha() {}

un peu plus court et plus propre, à mon ..

3
mihahh

Dans la version actuelle de Java, j'ai pu résoudre ce problème avec l'annotation suivante:

@Foo({"one", "two"})