为什么我收到错误“属性值必须是常量”。不是null常量吗???
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeInterface {
Class<? extends Foo> bar() default null;// this doesn't compile
}
null
不是常数吗?”答案是否定的,在 Java 中,null
不是常数。 null
不能在任何需要编译时常量的地方使用。例如,尝试将 String foo() default "" + null;
添加到您的注释中。我们知道,表达式的计算结果可预测为 "null"
,但它仍然不是常量表达式,因此是不允许的。
我不知道为什么,但 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 {};
}
所以基本上你创建一个空数组。这似乎允许使用默认值。
类 extends Foo> bar() default null;// 这不会编译
如前所述,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) { ... }
希望这可以帮助。
.equals()
而不是 ==
@Doradus?您是否获得了不同的 值 或不同的对象。
==
。
.equals()
。奇怪。
正如其他人所说,有一条规则说 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 final Class<?> myClass;
MyClassEnum(Class<?> aClass) {
myClass = aClass;
}
}
并更改注释:
public @interface MyAnnotation {
MyClassEnum value() default MyClassEnum.NULL;
}
这样做的主要缺点(除了需要枚举您可能的值之外)是您现在已经将您的值包装在一个枚举中,因此您需要调用枚举上的属性/getter 来获取值。
该错误消息具有误导性,但是,不,null
文字不是 Java 语言规范 here 中定义的常量表达式。
此外,Java Language Specification 说
如果元素的类型与指定的默认值不相称(第 9.7 节),则会出现编译时错误。
如果元素类型与元素值不相称,则为编译时错误。当且仅当以下条件之一为真时,元素类型 T 与元素值 V 相称: T 是数组类型 E[],并且: [...] T 不是数组类型,并且V 与 T 的赋值兼容(第 5.2 节),并且:如果 T 是原始类型或字符串,则 V 是常量表达式(第 15.28 节)。如果 T 是 Class 或 Class 的调用(第 4.5 节),则 V 是类文字(第 15.8.2 节)。如果 T 是枚举类型(第 8.9 节),则 V 是枚举常量(第 8.9.1 节)。 V 不为空。
所以在这里,您的元素类型 Class<? extends Foo>
与默认值不相称,因为该值是 null
。
or
和一个内部 and
,不能省略。
Class<? extends Foo> bar() default void.null
不编译。