Monday, April 16, 2012

Multiple annotations of the same type on one element?


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.


Source: Tips4all

3 comments:

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

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

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


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

    btw, I've just used this 2 hours ago to work around the same problem :)

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

    ReplyDelete
  3. @notnoop - Re jdk7, imagine a domain object class, and you want to specify several constraint expressions to be verified as class annotations.

    ReplyDelete