为注解字段设置默认空值时出错
为什么我得到一个错误“属性值必须是常量”。 不是空常量?
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface SomeInterface { Class<? extends Foo> bar() default null;// this doesn't compile }
我不知道为什么,但是JLS很清楚:
Discussion Note that null is not a legal element value for any element type.
而默认元素的定义是:
DefaultValue: default ElementValue
不幸的是,我不断地发现,当你不符合语言规范时,新的语言function(枚举和现在的批注)有非常无益的编译器错误信息。
编辑:有点googleing在JSR-308发现了以下内容 ,他们认为在这种情况下允许为null:
我们注意到一些可能的反对意见。
这个提议并没有做出任何以前不可能的事情。
程序员定义的特殊值提供比null更好的文档,这可能意味着“none”,“uninitialized”,null本身等等。
该提案更容易出错。 忘记检查空值比忘记检查显式值要容易得多。
这个build议可能会使标准习语更加冗长。 目前只有注释用户需要检查其特殊值。 有了这个build议,许多处理注释的工具将不得不检查一个字段的值是否为空,以免抛出空指针exception。
我认为只有最后两点与“为什么不先做”有关。 最后一点肯定会提出一个好的观点 – 注释处理程序永远不必担心它们在注释值上会得到空值。 我倾向于看到注解处理器和其他这样的框架代码的作用越来越多,必须做这样的检查,使开发人员的代码更清晰,而不是反其道而行,但它肯定会使改变它的理由变得困难。
尝试这个
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface SomeInterface { Class bar() default void.class; }
它不需要一个新的类,它已经是Java中的一个关键字,没有任何意义。
这似乎是非法的,尽pipeJLS在这方面非常模糊。
我用自己的记忆来试着去思考那里有一个注解的Class属性的注释,并从JAXB API中记住了这个:
@Retention(RUNTIME) @Target({PACKAGE,FIELD,METHOD,TYPE,PARAMETER}) public @interface XmlJavaTypeAdapter { Class type() default DEFAULT.class; static final class DEFAULT {} }
你可以看到他们如何定义一个虚拟的静态类来保存null的等价物。
不愉快。
看起来还有另外一种方法。
我也不喜欢这个,但它可能工作。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface SomeInterface { Class<? extends Foo>[] bar() default {}; }
所以基本上你创build一个空的数组。 这似乎允许一个默认值。
这个错误信息是误导性的,但是,不, null
字符不是一个常量expression式, 这里定义在Java语言规范中。
此外, Java语言规范说
如果元素的types与指定的默认值不相称 (§9.7),则是编译时错误。
并解释这是什么意思
如果元素types与元素值不相称,则为编译时错误。 元素typesT与元素值V相称当且仅当满足下列条件之一时:
- […]
- V不是null 。
所以在这里,你的元素typesClass<? extends Foo>
Class<? extends Foo>
,与默认值不相称,因为该值为null
。
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) { ... }
希望这可以帮助别人。