Чому не можна розширювати анотації на Java?


227

Я не розумію, чому в анотаціях Java немає успадкування, як і класи Java. Я думаю, це було б дуже корисно.

Наприклад: Я хочу знати, чи дана анотація є валідатором. З успадкуванням я міг би рефлекторно переходити через суперкласи, щоб знати, чи поширюється ця анотація ValidatorAnnotation. Інакше як я можу цього досягти?

Отже, чи може хтось дати мені причину такого дизайнерського рішення?


2
Зауважте, BTW, що всі анотації поширюються java.lang.annotation.Annotation, тобто будь-яка анотація є, instanceofхоча цей факт прямо не зазначається.
Томаш Залуський

Відповіді:


166

Про причину, чому вона не була розроблена таким чином, ви можете знайти відповідь у FAQ-дизайні JSR 175 , де написано:

Чому ви не підтримуєте підгрупування приміток (де один тип анотацій поширюється на інший)?

Це ускладнює систему типу анотацій і ускладнює написання "Спеціальних інструментів".

"Спеціальні інструменти" - програми, які запитують відомі типи анотацій довільних зовнішніх програм. Наприклад, генератори заглушок підпадають під цю категорію. Ці програми будуть читати анотовані класи, не завантажуючи їх у віртуальну машину, але завантажуватимуть інтерфейси анотацій.

Так, так, я гадаю, причина в тому, що це просто KISS. У будь-якому випадку, здається, що ця проблема (разом з багатьма іншими) розглядається як частина JSR 308 , і ви навіть можете знайти альтернативний компілятор з цією функціональністю, вже розроблений Матіасом Рікен .


67
Ну, можливо, я дурний, але я думаю, що шкода не може поширювати анотації лише для "простоти". Принаймні, дизайнери Java не думали однаково щодо спадкування класів: P
sinuhepop

2
Java 8 M7, схоже, не підтримує анотації підкласингу. Яка прикрість.
Чекі

2
@assylias JEP 104 - не про те, щоб зробити можливим підклас анотацій. JEP 104 був реалізований в Java 8, але все ще неможливо підкласировать анотації (зробити одну анотацію розширеною іншою анотацією).
Джеспер

71

Анотації, що розширюються, ефективно додають тягар визначення та обслуговування іншої системи. І це була б досить унікальна система типу, тому ви не могли просто застосувати парадигму типу ОО.

Подумайте над усіма питаннями, коли ви вносите поліморфізм та успадкування до анотації (наприклад, що відбувається, коли суб-анотація змінює специфікації метаанотації, такі як утримання?)

І все це додало складності для того, який випадок використання?

Ви хочете знати, чи належить дана примітка до категорії?

Спробуйте це:

@Target(ElementType.ANNOTATION_TYPE)
public @interface Category {
    String category();
}

@Category(category="validator")
public @interface MyFooBarValidator {

}

Як бачите, ви можете легко групувати та класифікувати анотації без зайвого болю за допомогою наданих засобів.

Отже, KISS є причиною невведення системи типу мета-типу до мови Java.

[ps редагувати]

Я використовував String просто для демонстрації та з огляду на відкриту мета-анотацію. Для власного даного проекту ви, очевидно, можете використовувати перелік типів категорій і вказати кілька категорій ("множинне спадкування") для даної анотації. Зауважте, що цінність є неправдивою і лише для демонстраційних цілей:

@Target(ElementType.ANNOTATION_TYPE)
public @interface Category {
    AnnotationCategory[] category();
}
public enum AnnotationCategory {
    GENERAL,
    SEMANTICS,
    VALIDATION,
    ETC
}

@Category(category={AnnotationCategory.GENERAL, AnnotationCategory.SEMANTICS})
public @interface FooBarAnnotation {

}


Не працює, друкується помилково:@Target(ElementType.ANNOTATION_TYPE) @Retention(RetentionPolicy.RUNTIME) @interface C {}; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @C public @interface F {} class a{ @F public void S() {} } @Test public void blahTest() throws NoSuchMethodException { Method m = a.class.getMethod("S"); System.out.println(m.isAnnotationPresent(C.class)); }
user1615664

Ми зазначаємо примітку. a # S має анотацію F. F себе позначає C. Спробуйте.
alphazero

Так, це правильно. Але чи є спосіб зробити це більш чисто, а не читати через проксі-анотацію?
користувач1615664

12

У певному сенсі у вас це вже є Анотації - мета Анотації. Якщо ви коментуєте примітку за допомогою метаінформації, це багато в чому еквівалентно розширенню додаткового інтерфейсу. Анотації - це інтерфейси, тому поліморфізм насправді не грає, і оскільки вони є статичними за своєю природою, не може бути динамічної диспетчеризації часу виконання.

У вашому прикладі валідатора ви можете просто на анотації отримати анотований тип і побачити, чи має він мета-анотацію валідатора.

Єдиний випадок використання, який я міг бачити, що успадкування допоможе, це якщо ви хочете отримати анотацію за супер типом, але це додасть цілу купу складності, оскільки даний метод або тип може мати дві такі примітки, що означає, що масив повинен бути повернутий замість просто одного об'єкта.

Тому я думаю, що остаточна відповідь полягає в тому, що випадки використання є езотеричними і ускладнюють більш стандартні випадки використання, тому не варто.


5

Дизайнери підтримки анотацій Java зробили ряд "спрощень" на шкоду спільноті Java.

  1. Відсутність підтипів анотацій робить багато складних приміток надмірно некрасивими. Не можна просто мати атрибут у примітках, що містять одну з трьох речей. Потрібно мати три окремі атрибути, що бентежать розробників і вимагають перевірки часу виконання, щоб гарантувати використання лише одного з трьох.

  2. Лише одна примітка певного типу на кожен сайт. Це призвело до абсолютно непотрібної картини анотацій колекції. @ Валідація та @ Валідації, @Image та @Images тощо.

Другий виправляється в Java 8, але його занадто пізно. Багато фреймворків було написано на основі того, що було можливо в Java 5, і тепер ці бородавки API тут, щоб залишитися на довгий час.


1
Мало того, це також робить неможливим визначення властивостей, які мають рекурсивно вкладені атрибути - що підтримується XML-схемою. Це робить анотації суворо менш потужними, ніж XML
user2833557

3

Я можу запізнитися на три роки у відповіді на це запитання, але мені це було цікаво, бо я опинився на тому самому місці. Ось мій погляд на це. Ви можете переглянути анотації як Enums. Вони надають односторонню інформацію - використовують її або втрачають.

У мене виникла ситуація, коли я хотів імітувати GET, POST, PUT та DELETE у веб-додатку. Я так сильно хотів мати "супер" анотацію, яка називалася "HTTP_METHOD". Пізніше на мені світалося, що це не мало значення. Ну, мені довелося вирішити використання прихованого поля у формі HTML для ідентифікації DELETE та PUT (адже POST та GET були доступні у будь-якому випадку).

На стороні сервера я шукав прихований параметр запиту з назвою "_method". Якщо значенням було PUT або DELETE, то воно замінило відповідний метод запиту HTTP. Сказавши це, неважливо, чи потрібно мені поширювати анотацію, щоб виконати роботу. Усі анотації виглядали однаково, але вони по-різному ставилися до сервера.

Тож у вашому випадку скиньте свербіж, щоб поширити анотації. Ставтесь до них як до «маркерів». Вони "представляють" певну інформацію, а не обов'язково "маніпулюють" деякою інформацією.


2

Я міг би подумати - це можливість мати кілька анотацій. Таким чином, ви можете додати валідатор та більш конкретну примітку в тому самому місці. Але я можу помилитися :)


2

Ніколи не замислювався над цим, але ... здається, ти маєш рацію, проблеми з спадщиною анотацій немає (принаймні, я не бачу проблеми з цим).

Про ваш приклад із анотацією 'validator' - тоді ви можете використовувати підхід «мета-анотації» . Тобто ви застосовуєте конкретну метаанотацію до всього інтерфейсу анотацій.


Я можу запізнитися на три роки у відповіді на це запитання, але мені це було цікаво, бо я опинився на тому самому місці.
mainas

1

та сама проблема, що і у мене. Ні, ти не можеш. Я "дисциплінував" себе писати властивості в анотаціях, щоб дотримуватися деяких стандартів, тому зовні, коли ви отримуєте анотацію, ви можете "обнюхати", який тип анотації він має за властивостями.

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