2009-10-12 20 views
75

Próbuję uderzyć dwie lub więcej adnotacji tego samego typu na jeden element, w tym przypadku, metodę. Oto przybliżona Kod że pracuję z:Wiele adnotacji tego samego typu na jednym elemencie?

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

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

Przy sporządzaniu powyższego, javac narzeka duplikatu adnotacji:

 
[email protected]:~/work/daybreak$ javac Dupe.java 
Dupe.java:5: duplicate annotation 

Jest on po prostu nie można powtórzyć adnotacji w ten sposób? Z pedantycznego punktu widzenia nie są dwa przypadki @Foo powyżej różnych ze względu na różne ich zawartości?

Jeśli powyższe nie jest możliwe, jakie są potencjalne sposoby obejścia problemu?

AKTUALIZACJA: Poproszono mnie o opisanie mojego przypadku użycia. Tutaj idzie.

Buduję mechanizm cukrowej składni, aby "mapować" POJO do sklepów z dokumentami, takich jak MongoDB. Chcę zezwolić na określanie indeksów jako adnotacji dla modułów pobierających lub ustawiających. Oto zmyślony przykład:

public class Employee { 
    private List<Project> projects; 

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

Oczywiście, chcę być w stanie szybko znaleźć wystąpień pracownikowi przez różne właściwości projektu. Mogę albo podać @Index dwa razy z różnymi wartościami expr(), albo przyjąć podejście określone w zaakceptowanej odpowiedzi. Mimo, że Hibernate to robi i nie jest to uważane za hackowanie, myślę, że nadal sensowne jest przynajmniej zezwolenie na posiadanie wielu adnotacji tego samego typu na jednym elemencie.

+1

Istnieje starań, aby ta podwójna reguła się rozluźniła, aby pozwolić na twój program w Javie 7. Czy możesz opisać swój przypadek użycia? – notnoop

+0

Edytowałem moje pytanie z opisem, dlaczego chcę to zrobić. Dzięki. –

+0

Może to być przydatne w CDI, aby umożliwić dostarczenie komponentu bean dla wielu kwalifikatorów. Na przykład próbowałem ponownie użyć komponentu bean w dwóch miejscach, kwalifikując go jako "@Produces @PackageName (" test1 ") @PackageName (" test2 ")" –

Odpowiedz

127

Dwie lub więcej adnotacji tego samego typu są niedozwolone. Jednak możesz zrobić coś takiego:

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

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

Będziesz potrzebował dedykowanej obsługi adnotacji Foos w kodzie.

btw, właśnie używane to 2 godziny temu, aby obejść ten sam problem :)

+2

Czy możesz to zrobić również w Groovy? – Excel20

+5

@ Excel20 Tak. Trzeba jednak użyć nawiasów kwadratowych, np. '@Foos ([@ Foo (bar =" jeden "), @Foo (bar =" dwa ")])'. Zobacz http://groovy.codehaus.org/Annotations+z+Groovy – sfussenegger

+0

Trochę za późno, ale staraj się wskazywać na porady, które mogą przetworzyć listę Foo w Foos? W tej chwili staram się uzyskać efekt metody, ale mimo, że Foos jest przechwytywany, porady Foo nigdy nie są wprowadzane. –

12

jak powiedział sfussenegger, to nie jest możliwe.

Typowym rozwiązaniem jest zbudowanie "wielorakiej" adnotacji, która obsługuje tablicę poprzedniej adnotacji. Zwykle nazywa się to samo, z sufiksem "s".

Nawiasem mówiąc, jest to bardzo użyteczne w dużych projektach publicznych (na przykład Hibernate), więc nie powinno być traktowane jako hack, ale raczej jako poprawne rozwiązanie dla tej potrzeby.


W zależności od potrzeb, może być lepiej umożliwić wcześniej adnotacja obsłużyć wiele wartości.

Przykład:

public @interface Foo { 
     String[] bars(); 
    } 
+0

Wiedziałem, że to było dziwnie znajome. Dzięki za odświeżenie mojej pamięci. –

+0

Teraz jest to możliwe dzięki Java 8. – AnixPasBesoin

3

Jeśli masz tylko 1 parametr "bar" można nazwać jako "wartości". W tym przypadku nie będziesz musiał wpisać nazwę parametru w ogóle, kiedy go używać tak:

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

nieco krótszy i schludniejszy, imho ..

+0

Prawidłowy punkt, ale jak ta próba odpowiedzi na pytanie OP? – Mordechai

+0

@MouseEvent masz rację, myślę, że to było więcej poprawy górnej odpowiedzi od sfussenegger i dlatego należy bardziej w komentarzach tam. Ale odpowiedź jest już nieaktualna ze względu na powtarzalne adnotacje ... – mihahh

3

łącząc inne odpowiedzi do najprostszej postaci ... adnotacją z prostym listy wartości ...

11

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

Począwszy od Java8 można opisać powtarzalne adnotacje:

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

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

Uwaga, value to pole wymagane dla listy wartości.

Teraz można użyć opisów powtarzając je zamiast napełniania tablicy:

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

Oprócz innych sposobów wymienionych istnieje jeszcze mniej rozwlekły sposób 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{ 

} 

przykład przez domyślnie dostaje, FooContainer jako adnotacja

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

Zarówno powyżej druku:

@ com.FooContainer (wartość = [@ com.Foo (wartość = 1) @ com.Foo (wartość = 2), @ com.Foo (wartość = 3)])

@ com.FooContainer (wartość = [@ com.Foo (wartość = 1) @ com.Foo (wartość = 2) @ com.Foo (wartość = 3)])

+0

Warto wskazać, że nazwa metody/pola w FooContainer musi być ściśle nazywane "value()". W przeciwnym razie Foo nie będzie się kompilował. – Tomasz

+0

... również zawierający typ adnotacji (FooContainer) nie może być stosowany do większej liczby typów niż powtarzalny typ adnotacji (Foo). – Tomasz

Powiązane problemy