Несколько аннотаций одного типа на один элемент? - PullRequest
82 голосов
/ 12 октября 2009

Я пытаюсь наложить две или более аннотации одного типа на один элемент, в данном случае метод. Вот примерный код, с которым я работаю:

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

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

При компиляции вышеизложенного javac жалуется на двойную аннотацию:

max@upsight:~/work/daybreak$ javac Dupe.java 
Dupe.java:5: duplicate annotation

Разве просто невозможно повторить подобные аннотации? Говоря педантично, не отличаются ли два экземпляра @Foo выше из-за их различного содержания?

Если вышеперечисленное невозможно, каковы возможные обходные пути?

ОБНОВЛЕНИЕ: меня попросили описать мой вариант использования. Здесь идет.

Я создаю синтаксический сахарный механизм для «сопоставления» POJO с хранилищами документов, такими как MongoDB. Я хочу, чтобы индексы указывались как аннотации для методов получения или установки. Вот надуманный пример:

public class Employee {
    private List<Project> projects;

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

Очевидно, я хочу иметь возможность быстро находить экземпляры Employee по различным свойствам Project. Я могу либо указать @Index дважды с разными значениями expr (), либо использовать подход, указанный в принятом ответе. Несмотря на то, что Hibernate делает это, и это не считается хаком, я думаю, что все же имеет смысл хотя бы разрешить иметь несколько аннотаций одного типа на одном элементе.

Ответы [ 8 ]

133 голосов
/ 12 октября 2009

Две или более аннотации одного типа не допускаются. Тем не менее, вы можете сделать что-то вроде этого:

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

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

Вам потребуется специальная обработка аннотации Foos в коде.

Кстати, я только что использовал это 2 часа назад, чтобы обойти ту же проблему:)

58 голосов
/ 12 сентября 2012

В Java 8 (выпущена в марте 2014 г.) можно писать повторяющиеся / дублирующиеся аннотации. Смотри http://docs.oracle.com/javase/tutorial/java/annotations/repeating.html.

14 голосов
/ 19 марта 2014

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

Начиная с Java8, вы можете описать повторяющиеся аннотации:

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

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

Примечание, value обязательное поле для списка значений.

Теперь вы можете использовать аннотации, повторяя их вместо заполнения массива:

@Foo(bar="one")
@Foo(bar="two")
public void haha() {}
13 голосов
/ 22 февраля 2015

Помимо других упомянутых способов, в 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{

}

Пример по умолчанию получает, FooContainer в качестве аннотации

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

Обе вышеперечисленные печати:

@com.FooContainer (значение = [@ com.Foo (значение = 1), @ com.Foo (значение = 2), @ com.Foo (значение = 3)])

@com.FooContainer (значение = [@ com.Foo (значение = 1), @ com.Foo (значение = 2), @ com.Foo (значение = 3)])

12 голосов
/ 12 октября 2009

Как сказал sfussenegger, это невозможно.

Обычное решение - создать «множественную» аннотацию , которая обрабатывает массив предыдущей аннотации. Обычно он называется одинаково, с суффиксом 's'.

Кстати, это очень используется в больших публичных проектах (например, в Hibernate), поэтому его не следует рассматривать как взлом, а скорее как правильное решение для этой необходимости.


В зависимости от ваших потребностей, может быть лучше разрешить вашей предыдущей аннотации обрабатывать несколько значений .

Пример:

    public @interface Foo {
      String[] bars();
    }
4 голосов
/ 21 ноября 2013

объединение остальных ответов в простейшую форму ... аннотация с простым списком значений ...

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

//...

public @interface Foos{
    String[] value();
}
3 голосов
/ 16 апреля 2012

Если у вас есть только 1 параметр «bar», вы можете назвать его «value». В этом случае вам не нужно будет писать имя параметра вообще, когда вы используете его так:

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

немного короче и аккуратнее, имхо ..

0 голосов
/ 27 ноября 2018

В текущей версии Java мне удалось решить эту проблему с помощью следующей аннотации:

@Foo({"one", "two"})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...