Ошибка установки значения по умолчанию для поля аннотации
Почему я получаю сообщение об ошибке "Значение атрибута должно быть постоянным". Не является константой 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) { ... }
Надеюсь, это поможет кому-то еще.