为什么会出现错误“属性值必须为常数”。 null常量不是吗?
@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
不幸的是,当您不符合语言规范时,我不断发现新的语言功能(枚举和现在的注释)具有非常无用的编译器错误消息。
编辑:一点点搜索在JSR-308中发现了following,他们在这种情况下主张允许空值:
我们注意到对该提案可能存在的反对意见。
该提案无法解决以前无法实现的任何事情。
程序员定义的特殊值比null提供更好的文档,这可能意味着“无”,“未初始化”,null本身,等等。
该建议更容易出错。忘记检查空值比忘记检查显式值要容易得多。
该建议可能会使标准习语更加冗长。当前,只有注释的用户需要检查其特殊值。有了这个提议,许多处理注释的工具将不得不检查字段的值是否为空,以免它们抛出空指针异常。
我认为只有最后两点与“为什么一开始就不这样做”有关。最后一点肯定会带来好处-注释处理器永远不必担心它们将在注释值上获得null。我倾向于看到,随着注释处理器和其他此类框架代码的工作越来越多,必须进行这种检查才能使开发人员的代码更清晰,而不是反过来,但这无疑会使更改它变得不合理。
尝试一下
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeInterface {
Class bar() default void.class;
}
它不需要新的类,它已经是Java中的关键字,什么都没有。
这似乎是非法的,尽管JLS对此非常模糊。
我不知所措,想一想那里的现有注释具有注释的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 {};
}
因此,基本上,您创建一个空数组。这似乎允许使用默认值。
Class<? extends Foo> bar() default null;// this doesn't compile
如上所述,Java语言规范不允许在注释默认值中使用空值。
我倾向于做的是在注释定义的顶部定义DEFAULT_VALUE
常量。类似于:
public @interface MyAnnotation {
public static final String DEFAULT_PREFIX = "__class-name-here__ default";
...
public String prefix() default DEFAULT_PREFIX;
}
然后在我的代码中,我做类似的事情:
if (myAnnotation.prefix().equals(MyAnnotation.DEFAULT_PREFIX)) { ... }
对于您的类,我只定义一个标记类。不幸的是,您不能为此设置常量,因此需要执行以下操作:
public @interface MyAnnotation {
public static Class<? extends Foo> DEFAULT_BAR = DefaultBar.class;
...
Class<? extends Foo> bar() default DEFAULT_BAR;
}
您的DefaultFoo
类将只是一个空的实现,以便代码可以执行:
if (myAnnotation.bar() == MyAnnotation.DEFAULT_BAR) { ... }
希望这会有所帮助。
该错误消息具有误导性,但不,null
文字不是Java语言规范here中定义的常量表达式。
此外,Java Language Specification说
如果元素不相称(§9.7)的类型具有指定的默认值,则是编译时错误。
如果元素类型不适当,则是编译时错误与元素值。 元素类型T与元素值V当且仅当以下条件之一为真:
T
是数组类型E[]
,并且可以:
- [...]
T
不是数组类型,并且V
的类型与T
,和的分配兼容(第5.2节):
- 如果
T
是原始类型或String
,则V
是常数表达式(第15.28节)。- 如果
T
为Class
或对Class
的调用(第4.5节),则V
是类文字(第15.8.2节)。- 如果
T
是枚举类型(第8.9节),则V
是枚举常量(第8.9.1节)。- V不为空。
因此,您的元素类型Class<? extends Foo>
与默认值不符,因为该值为null
。
正如其他人所说,有一条规则说null在Java中不是有效的默认值。
如果该字段可以具有的有限数量的可能值,那么解决此问题的一种方法是使字段类型成为自定义枚举,该枚举本身包含对null的映射。例如,假设我有以下注释,我希望为其提供默认的null:
public @interface MyAnnotation {
Class<?> value() default null;
}
我们已经确定,这是不允许的。我们可以做的是定义一个枚举:
public enum MyClassEnum {
NULL(null),
MY_CLASS1(MyClass1.class),
MY_CLASS2(MyClass2.class),
;
public Class<?> myClass;
MyClassEnum(Class<?> aClass) {
myClass = aClass;
}
}
并这样更改注释:
public @interface MyAnnotation {
MyClassEnum value() default MyClassEnum.NULL;
}
此方法的主要缺点(除了需要枚举可能的值之外,还在于您现在将值包装在枚举中,因此您需要在枚举上调用属性/获取程序以获取该值。) >