Ошибка установки значения по умолчанию для поля аннотации

Почему я получаю сообщение об ошибке "Значение атрибута должно быть постоянным". Не является константой null

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeInterface {
    Class<? extends Foo> bar() default null;// this doesn't compile
}

Ответ 1

Я не знаю почему, но JLS очень понятен:

 Discussion

 Note that null is not a legal element value for any element type. 

И определение элемента по умолчанию:

     DefaultValue:
         default ElementValue

К сожалению, я продолжаю обнаруживать, что новые функции языка (Enums и now Annotations) имеют очень бесполезные сообщения об ошибках компилятора, когда вы не соответствуете спецификациям языка.

EDIT: небольшая googleing обнаружила после в JSR-308, где они утверждают, что допускают нули в этой ситуации:

Мы отмечаем некоторые возможные возражения против предложения.

Предложение не делает ничего возможного, что было невозможно раньше.

Специальное значение, определяемое программистом, обеспечивает лучшую документацию, чем нуль, что может означать "none", "uninitialized", null и т.д.

Предложение более подвержено ошибкам. Гораздо проще забыть проверку на нуль, чем забыть проверить явное значение.

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

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

Ответ 2

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

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeInterface {
    Class bar() default void.class;
}

Он не требует нового класса, и он уже является ключевым словом в Java, что ничего не значит.

Ответ 3

Казалось бы, это незаконно, хотя JLS очень нечеткая.

Я испортил свою память, чтобы попытаться подумать о существующей аннотации, которая содержит атрибут Class для аннотации, и вспомнила об этом из API JAXB:

@Retention(RUNTIME) @Target({PACKAGE,FIELD,METHOD,TYPE,PARAMETER})        
public @interface XmlJavaTypeAdapter {
    Class type() default DEFAULT.class;

    static final class DEFAULT {}    
}

Вы можете увидеть, как они должны были определить фиктивный статический класс для хранения эквивалента null.

Неприятно.

Ответ 4

Кажется, есть еще один способ сделать это.

Мне тоже это не нравится, но это может сработать.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeInterface {
    Class<? extends Foo>[] bar() default {};
}

Таким образом, в основном вы создаете пустой массив. Это, по-видимому, позволяет использовать значение по умолчанию.

Ответ 5

Это сообщение об ошибке вводит в заблуждение, но нет, литерал null не является постоянным выражением, как определено в Спецификации языка Java, здесь.

Кроме того, Спецификация Java Language говорит

Это ошибка времени компиляции, если тип элемента не является соразмерным (§9.7) со значением по умолчанию.

И чтобы объяснить, что это значит

Это ошибка времени компиляции, если тип элемента не соразмерен с величиной элемента. Тип элемента T соизмерим с значение элемента V тогда и только тогда, когда выполняется одно из следующих условий:

  • [...]
  • V не является нулевым.

Итак, ваш тип элемента Class<? extends Foo> не соизмерим со значением по умолчанию, потому что это значение null.

Ответ 6

Class<? extends Foo> bar() default null;// this doesn't compile

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

То, что я имею в виду, - это определить константы DEFAULT_VALUE в верхней части определения аннотации. Что-то вроде:

public static final String DEFAULT_VALUE = "__class-name-here__ default";

public String prefix() default DEFAULT_VALUE;

Затем в моем коде я делаю что-то вроде:

if (myAnnotation.prefix().equals(MyAnnotation.DEFAULT_VALUE)) { ... }

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

Class<? extends Foo> bar() default DefaultFoo.class;

Ваш класс DefaultFoo будет просто пустой реализацией, чтобы код мог:

if (myAnnotation.bar() == DefaultFoo.class) { ... }

Надеюсь, это поможет кому-то еще.