Wiele adnotacji tego samego typu na jednym elemencie?


91

Próbuję uderzyć dwie lub więcej adnotacji tego samego typu na jednym elemencie, w tym przypadku na metodzie. Oto przybliżony kod, z którym pracuję:

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

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

Podczas kompilowania powyższego javac narzeka na zduplikowaną adnotację:

max @ upsight: ~ / work / daybreak $ javac Dupe.java 
Dupe.java:5: zduplikowana adnotacja

Czy po prostu nie można powtórzyć takich adnotacji? Mówiąc pedantycznie, czy te dwa wystąpienia @Foo powyżej nie są różne, ponieważ ich zawartość jest inna?

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

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

Buduję mechanizm łagodnej składni, aby „mapować” POJO na magazyny dokumentów, takie jak MongoDB. Chcę zezwolić na określanie indeksów jako adnotacji na elementach pobierających lub ustawiających. Oto wymyś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ę móc szybko znaleźć wystąpienia pracownika według różnych właściwości projektu. Mogę określić @Index dwukrotnie z różnymi wartościami wyrażenia () lub zastosować podejście określone w zaakceptowanej odpowiedzi. Mimo że Hibernate to robi i nie jest to uważane za hack, myślę, że warto przynajmniej zezwolić na posiadanie wielu adnotacji tego samego typu na jednym elemencie.


1
Podjęto próbę, aby ta zduplikowana reguła została rozluźniona, aby pozwolić programowi w Javie 7. Czy możesz opisać swój przypadek użycia?
notnoop

Zmodyfikowałem moje pytanie, podając opis, dlaczego chcę to zrobić. Dzięki.
Max A.

W CDI może być przydatne, aby umożliwić dostarczanie fasoli dla wielu kwalifikatorów. Na przykład właśnie próbowałem ponownie użyć fasoli w dwóch miejscach, kwalifikując ją „@Produces @PackageName (" test1 ") @PackageName (" test2 ")"
Richard Corfield

Dalej: Poniższa odpowiedź nie rozwiązuje tego problemu, ponieważ CDI postrzegałoby kompozyt jako jeden kwalifikator.
Richard Corfield,

@MaxA. Zmień akceptację „zielonego czeku” na poprawną odpowiedź mernst. Java 8 dodała możliwość powtarzania adnotacji.
Basil Bourque

Odpowiedzi:


140

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

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

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

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

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


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

5
@ Excel20 Tak. Musiałbyś jednak użyć nawiasów kwadratowych, np @Foos([@Foo(bar="one"), @Foo(bar="two")]). Zobacz groovy.codehaus.org/Annotations+with+Groovy
sfussenegger

Trochę późno, ale czy chcesz wskazać porady, które mogą przetworzyć listę Foo w Foos? W tej chwili próbuję uzyskać wynik metody, ale chociaż Foos jest przechwycony, rada Foo nigdy nie jest wprowadzana
Stelios Koussouris

Aktualizacja: od wersji Java 8 ta odpowiedź jest nieaktualna. Zobacz poprawną nowoczesną odpowiedź mernst. Java 8 dodała możliwość powtarzania adnotacji.
Basil Bourque


21

Oprócz innych wymienionych sposobów, w Javie8 jest jeszcze jeden mniej rozwlekły sposób:

@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 domyślnie pobiera FooContainerjako adnotacja

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

Obie powyższe wydruki:

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


Warto zwrócić uwagę, że nazwa metody / pola w FooContainer musi być ściśle określona jako „wartość ()”. W przeciwnym razie Foo nie skompiluje się.
Tomasz Mularczyk

... również zawierający typ adnotacji (FooContainer) nie może mieć zastosowania do większej liczby typów niż typ powtarzalnej adnotacji (Foo).
Tomasz Mularczyk


12

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

Typowym rozwiązaniem jest utworzenie adnotacji „wielokrotnej” , która obsługuje tablicę poprzedniej adnotacji. Zwykle ma taką samą nazwę, z przyrostkiem „s”.

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


W zależności od potrzeb może być lepiej, jeśli wcześniejsza adnotacja obsługiwała wiele wartości .

Przykład:

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

Wiedziałem, że to niesamowicie znajome. Dzięki za odświeżenie mojej pamięci.
Max A.

Jest to teraz możliwe z Javą 8.
Anis

4

połączenie innych odpowiedzi w najprostszą formę ... adnotacja z prostą listą wartości ...

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

//...

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

3

Jeśli masz tylko 1 parametr „słupek”, możesz go nazwać „wartością”. W takim przypadku nie będziesz musiał w ogóle wpisywać nazwy parametru, gdy używasz jej w ten sposób:

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

trochę krótszy i schludniejszy, imho ...


Słuszna uwaga, ale jak to próbuje odpowiedzieć na pytanie OP?
Mordechai,

@MouseEvent masz rację, myślę, że było to bardziej ulepszenie górnej odpowiedzi od sfusseneggera, a zatem bardziej należy ją tam w komentarzach. Ale odpowiedź i tak jest nieaktualna z powodu powtarzających się adnotacji ...
golwig

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.