100

I'm attempting to slap two or more annotations of the same type on a single element, in this case, a method. Here's the approximate code that I'm working with:

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

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

When compiling the above, javac complains about a duplicate annotation:

max@upsight:~/work/daybreak$ javac Dupe.java 
Dupe.java:5: duplicate annotation

Is it simply not possible to repeat annotations like this? Pedantically speaking, aren't the two instances of @Foo above different due to their contents being different?

If the above isn't possible, what are some potential workarounds?

UPDATE: I've been asked to describe my use case. Here goes.

I'm building a syntax sugarish mechanism to "map" POJOs to document stores such as MongoDB. I want to allow indexes to be specified as annotations on the getters or setters. Here's a contrived example:

public class Employee {
    private List<Project> projects;

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

Obviously, I want to be able to quickly find instances of Employee by various properties of Project. I can either specify @Index twice with different expr() values, or take the approach specified in the accepted answer. Even though Hibernate does this and it's not considered a hack, I think it still makes sense to at least allow having multiple annotations of the same type on a single element.

Max A.
  • 4,842
  • 6
  • 29
  • 27
  • 1
    There is an effort to have this duplicate rule relax to permit your program in Java 7. Can you describe your use case please? – notnoop Oct 12 '09 at 12:07
  • I've edited my question with a description of why I want to do this. Thanks. – Max A. Oct 12 '09 at 13:46
  • It could be handy in CDI to allow a bean to be provided for multiple qualifiers. For example I've just tried to reuse a bean in two places by qualifying it "@Produces @PackageName("test1") @PackageName("test2")" – Richard Corfield Mar 05 '12 at 11:13
  • Further: The answer below does not solve that problem because CDI would see the composite as one Qualifier. – Richard Corfield Mar 05 '12 at 11:21
  • @MaxA. Please change your "green check" acceptance to [the correct Answer](https://stackoverflow.com/a/12394501/642706) by mernst. Java 8 added the ability to repeat annotations. – Basil Bourque Oct 31 '20 at 23:22

8 Answers8

147

Note: This answer is partially outdated since Java 8 introduced the @Repeatable annotation (see answer by @mernst). The need for a @Foos container annotation and dedicated handling still remain though.

Two or more annotations of same type aren't allowed. However, you could do something like this:

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

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

// post Java 8 with @Repeatable(Foos.class) on @Foo
@Foo(bar="one") @Foo(bar="two")
public void haha() {}

You'll need dedicated handling of Foos annotation in code though.

sfussenegger
  • 35,575
  • 15
  • 95
  • 119
  • 2
    Can you also do this in Groovy? – astellin Jan 11 '12 at 08:52
  • 5
    @Excel20 Yes. You'd have to use square brackets though, e.g. `@Foos([@Foo(bar="one"), @Foo(bar="two")])`. See http://groovy.codehaus.org/Annotations+with+Groovy – sfussenegger Jan 11 '12 at 13:29
  • A bit late in the day but care to point to the advice that can process the list of Foo inside Foos? At the moment I am trying to prcess the result of a method but although the Foos is intercepted the Foo advice is never entered – Stelios Koussouris Sep 05 '13 at 05:30
  • 1
    **Update:** As of Java 8, this Answer is outdated. See [the correct modern Answer](https://stackoverflow.com/a/12394501/642706) by mernst. Java 8 added the ability to repeat annotations. – Basil Bourque Oct 31 '20 at 23:28
  • Is possible to add a "@Foo" value as a constant? or how you can access each one? I try to use this something like "@StringDef" and define an "@interface" from another. – Gabriel Perez Jan 07 '21 at 19:32
74

Repeating annotations in Java 8

In Java 8 (released in March 2014), it is possible to write repeated/duplicate annotations.

See tutorial, Repeating Annotations.

See specification, JEP 120: Repeating Annotations.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
mernst
  • 7,437
  • 30
  • 45
27

Apart from the other ways mentioned, there is one more less verbose way in 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{

}

Example by default gets, FooContainer as an Annotation

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

Both the above print:

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

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

Community
  • 1
  • 1
Jatin
  • 31,116
  • 15
  • 98
  • 163
18

http://docs.oracle.com/javase/tutorial/java/annotations/repeating.html

Starting from Java8 you can describe repeatable annotations:

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

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

Note, value is required field for values list.

Now you can use annotations repeating them instead of filling the array:

@Foo(bar="one")
@Foo(bar="two")
public void haha() {}
Sergei Pikalev
  • 516
  • 4
  • 5
12

As said by sfussenegger, this isn't possible.

The usual solution is to build an "multiple" annotation, that handles an array of the previous annotation. It is typically named the same, with an 's' suffix.

By the way, this is very used in big public projects (Hibernate for example), so it shouldn't be considered as a hack, but rather a correct solution for this need.


Depending on your needs, it could be better to allow your earlier annotation to handle multiple values.

Example:

    public @interface Foo {
      String[] bars();
    }
KLE
  • 23,689
  • 4
  • 56
  • 62
4

combining the other answers into the simplest form ... an annotation with a simple list of values ...

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

//...

public @interface Foos{
    String[] value();
}
matt1616
  • 433
  • 4
  • 8
3

If you have only 1 parameter "bar" you can name it as "value". In this case you wont have to write the parameter name at all when you use it like this:

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

a bit shorter and neater, imho..

golwig
  • 109
  • 1
  • 7
  • Correct point, but how does this attempt to answer OP's question? – Mordechai Jan 10 '17 at 02:53
  • @MouseEvent you are right, i think it was more of an improvement of the top answer from sfussenegger and thus belongs more in the comments up there. But the answer is outdated anyway due to repeatable annotations... – golwig Jan 11 '17 at 22:09
0

In the current version of Java, I was able to resolve this issue with the following annotation:

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