Почему невозможно расширить аннотации в Java?

227

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

Например: я хочу знать, является ли данная аннотация валидатором. С наследованием я мог бы рефлексивно перемещаться по суперклассам, чтобы знать, расширяет ли эта аннотация a ValidatorAnnotation. Иначе как мне этого добиться?

Итак, кто-нибудь может дать мне причину этого дизайнерского решения?

sinuhepop
источник
2
Обратите внимание, что все аннотации расширяются java.lang.annotation.Annotation, т. Е. Любая аннотация, instanceofхотя этот факт явно не объявлен.
Томаш Залуски

Ответы:

166

О причине, по которой он не был спроектирован таким образом, вы можете найти ответ в FAQ по JSR 175 Design, где говорится:

Почему вы не поддерживаете подтипы аннотаций (когда один тип аннотации расширяет другой)?

Это усложняет систему типов аннотаций и значительно усложняет написание «Конкретных инструментов».

...

«Специальные инструменты» - программы, которые запрашивают известные типы аннотаций произвольных внешних программ. Например, генераторы-заглушки попадают в эту категорию. Эти программы будут читать аннотированные классы без загрузки их в виртуальную машину, но будут загружать интерфейсы аннотаций.

Так что, да, я думаю, причина в том, что это просто ПОЦЕЛУЙ. Во всяком случае, кажется, что эта проблема (наряду со многими другими) рассматривается как часть JSR 308 , и вы даже можете найти альтернативный компилятор с этой функциональностью, уже разработанной Матиасом Рикеном .

pedromarce
источник
67
Ну, может, я тупой, но мне жаль, что я не могу расширять аннотации только для того, чтобы "было проще". По крайней мере, разработчики Java не думали так же о наследовании классов: P
sinuhepop
2
Java 8 M7, похоже, не поддерживает аннотации подклассов. Какая жалость.
Ceki
2
@assylias JEP 104 не делает возможным создание подклассов аннотаций. JEP 104 был реализован в Java 8, но все еще невозможно разделить аннотации на подклассы (заставить одну аннотацию расширять другую аннотацию).
Джеспер
71

Расширяемые аннотации эффективно добавили бы бремя определения и поддержки другой системы типов. И это будет довольно уникальная система типов, поэтому вы не сможете просто применить парадигму типа ОО.

Продумайте все вопросы, когда вы вводите полиморфизм и наследование в аннотацию (например, что происходит, когда поданнотация изменяет спецификации метааннотации, такие как сохранение?)

И все это добавляет сложности для какого варианта использования?

Вы хотите знать, относится ли данная аннотация к категории?

Попробуй это:

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

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

}

Как вы можете видеть, вы можете легко группировать и классифицировать аннотации без лишних усилий, используя предоставленные средства.

Итак, поцелуй является причиной отказа от введения системы типов мета-типов в язык Java.

[ps edit]

Я использовал 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 {

}

alphazero
источник
Не работает будет печатать false:@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
Да, это правильно. Но есть ли способ сделать это более чисто, чем чтение через прокси-сервер аннотаций?
user1615664
12

В некотором смысле у вас уже есть это с аннотациями - мета аннотации. Если вы аннотируете аннотацию метаинформацией, это во многом эквивалентно расширению дополнительного интерфейса. Аннотации являются интерфейсами, поэтому полиморфизм на самом деле не вступает в игру, и поскольку они статичны по своей природе, динамическая диспетчеризация во время выполнения не может быть.

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

Единственный вариант использования, который я мог видеть, что наследование помогло бы, это если бы вы хотели иметь возможность получать аннотации по супертипу, но это добавило бы целую кучу сложности, потому что у данного метода или типа могут быть две такие аннотации, Это означает, что массив должен быть возвращен вместо одного объекта.

Поэтому я думаю, что окончательный ответ заключается в том, что варианты использования являются эзотерическими и усложняют более стандартные варианты использования, что делает его не стоящим.

Ишай
источник
5

Разработчики поддержки аннотаций Java сделали ряд «упрощений» в ущерб сообществу Java.

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

  2. Только одна аннотация данного типа для каждого сайта. Это привело к совершенно ненужному шаблону аннотации коллекции. @Validation и @Validations, @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-запроса. Сказав это, не имело значения, нужно ли мне расширять аннотацию, чтобы выполнить работу. Все аннотации выглядели одинаково, но на стороне сервера они обрабатывались по-разному.

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

mainas
источник
2

Одна вещь, о которой я мог подумать, это возможность иметь несколько аннотаций. Таким образом, вы можете добавить валидатор и более конкретную аннотацию в одном месте. Но я могу ошибаться :)

Януш
источник
2

Никогда не думал об этом, но ... кажется, что вы правы, с наследованием аннотаций проблем нет (по крайней мере, я не вижу проблем с этим).

О вашем примере с аннотацией 'validator' - тогда вы можете использовать подход 'meta-annotation' . Т.е. вы применяете определенную метааннотацию ко всему интерфейсу аннотации.

denis.zhdanov
источник
Я мог бы опоздать на три года с ответом на этот вопрос, но я нашел его интересным, потому что я оказался в том же месте.
Майна
1

та же проблема у меня. Нет, ты не можешь. Я «дисциплинировал» себя, чтобы писать свойства в аннотациях, чтобы соблюдать некоторые стандарты, поэтому снаружи, когда вы получаете аннотацию, вы можете «узнать», какой это вид аннотации, по свойствам, которые она имеет.

ante.sabo
источник