Кілька анотацій одного типу на одному елементі?


91

Я намагаюся ляпати дві або більше анотацій одного типу на одному елементі, в даному випадку методу. Ось приблизний код, з яким я працюю:

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: дубльована анотація

Чи просто не можна повторювати подібні анотації? Педантично кажучи, чи не відрізняються обидва випадки @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 робить це, і це не вважається хакерством, я вважаю, що все одно має сенс принаймні дозволити мати кілька анотацій одного типу на одному елементі.


1
Існує спроба послабити це повторюване правило, щоб дозволити програму на Java 7. Чи можете ви описати ваш варіант використання?
— notnoop

Я відредагував своє запитання з описом, чому я хочу це зробити. Дякую.
— Макс А.

У CDI може бути дуже зручно, щоб забезпечити надання квасолі для кількох кваліфікацій. Наприклад, я щойно намагався повторно використати боб у двох місцях, визначивши його "@Produces @PackageName (" test1 ") @PackageName (" test2 ")"
— Річард Корфілд,

Далі: Наведена нижче відповідь не вирішує цю проблему, оскільки CDI бачить композит як один Кваліфікатор.
— Річард Корфілд,

@MaxA. Будь ласка, змініть своє прийняття "зеленого чека" на правильну відповідь mernst. Java 8 додала можливість повторення анотацій.
— Василь Бурк

Відповіді:


140

Дві або більше анотацій одного типу заборонені. Однак ви можете зробити щось подібне:

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

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

Однак вам знадобиться спеціальна обробка анотацій Foos в коді.

До речі, я щойно використав це 2 години тому, щоб обійти ту ж проблему :)


2
Чи можете ви це зробити також у Groovy?
— Excel20,

5
@ Excel20 Так. Вам доведеться використовувати квадратні дужки, наприклад, наприклад @Foos([@Foo(bar="one"), @Foo(bar="two")]). Дивіться groovy.codehaus.org/Annotations+with+Groovy
— sfussenegger

Трохи пізно вдень, але хочете вказати на пораду, яка може обробити список Foo всередині Foos? На даний момент я намагаюся визначити результат методу, але, незважаючи на те, що Foos перехоплений, порада Foo ніколи не надходить
— Stelios Koussouris

Оновлення: станом на Java 8 ця відповідь застаріла. Дивіться правильну сучасну відповідь mernst. Java 8 додала можливість повторення анотацій.
— Василь Бурк

65

Повторювані анотації в Java 8

У Java 8 (випущена в березні 2014 року) можна писати повторювані / повторювані анотації.

Див. Підручник, Повторювані анотації .

Див. Специфікацію, JEP 120: Повторювані анотації .



21

Окрім інших згаданих способів, у 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)])


Варто зазначити, що ім'я методу / поля у FooContainer повинно мати строгу назву "value ()". Інакше Foo не буде компілювати.
— Томаш Муларчик

... також містить тип анотації (FooContainer) не може застосовуватися до більшої кількості типів, ніж тип повторюваних анотацій (Foo).
— Томаш Муларчик

16

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() {}

12

Як сказав sfussenegger, це неможливо.

Звичайним рішенням є створення "множинної" анотації , яка обробляє масив попередньої анотації. Зазвичай його називають однаковим, із суфіксом 's'.

До речі, це дуже використовується у великих державних проектах (наприклад, Hibernate), тому це не слід розглядати як хакерство, а скоріше правильне рішення для цієї потреби.


Залежно від ваших потреб, може бути краще дозволити вашій попередній анотації обробляти кілька значень .

Приклад:

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

Я знав, що це моторошно знайоме. Дякую за освіження моєї пам’яті.
— Макс А.

Тепер це можливо з Java 8.
— Аніс,

4

об'єднання інших відповідей у ​​найпростішу форму ... анотація з простим списком значень ...

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

//...

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

3

Якщо у вас є лише 1 параметр "bar", ви можете назвати його "value". У цьому випадку вам не доведеться взагалі писати ім'я параметра, коли ви використовуєте його таким чином:

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

трохи коротший і акуратніший, імхо ..


Правильний момент, але як ця спроба відповісти на питання ОП?
— Мордехай

@MouseEvent ви маєте рацію, я думаю, це було скоріше вдосконаленням головної відповіді sfussenegger і, отже, більше належить там у коментарях. Але відповідь у будь-якому випадку застаріла через повторювані анотації ...
— golwig

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.