Multiple annotations of the same type on one element?

JavaAnnotations

Java Problem Overview


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.

Java Solutions


Solution 1 - Java

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.

Solution 2 - Java

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.

Solution 3 - Java

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)])

Solution 4 - Java

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() {}

Solution 5 - Java

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();
    }

Solution 6 - Java

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();
}

Solution 7 - Java

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..

Solution 8 - Java

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

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMax A.View Question on Stackoverflow
Solution 1 - JavasfusseneggerView Answer on Stackoverflow
Solution 2 - JavamernstView Answer on Stackoverflow
Solution 3 - JavaJatinView Answer on Stackoverflow
Solution 4 - JavaSergei PikalevView Answer on Stackoverflow
Solution 5 - JavaKLEView Answer on Stackoverflow
Solution 6 - Javamatt1616View Answer on Stackoverflow
Solution 7 - JavagolwigView Answer on Stackoverflow
Solution 8 - JavaEduardo Alexandre de SouzaView Answer on Stackoverflow