Reputation: 4892
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.
Upvotes: 102
Views: 103144
Reputation: 36115
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.
Upvotes: 147
Reputation: 8147
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.
Upvotes: 77
Reputation: 11
In the current version of Java, I was able to resolve this issue with the following annotation:
@Foo({"one", "two"})
Upvotes: 0
Reputation: 31754
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)])
Upvotes: 29
Reputation: 516
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() {}
Upvotes: 18
Reputation: 502
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();
}
Upvotes: 4
Reputation: 139
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..
Upvotes: 3
Reputation: 24169
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();
}
Upvotes: 12