为注解字段设置默认空值时出错

为什么我得到一个错误“属性值必须是常量”。 不是常量?

@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) { ... } 

希望这可以帮助别人。