为什么会出现错误“属性值必须恒定”。是不是空常量???
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeInterface {
Class<? extends Foo> bar() default null;// this doesn't compile
}
Answers:
我不知道为什么,但是JLS很清楚:
Discussion
Note that null is not a legal element value for any element type.
默认元素的定义是:
DefaultValue:
default ElementValue
不幸的是,当您不符合语言规范时,我不断发现新的语言功能(枚举和现在的注释)具有非常无用的编译器错误消息。
编辑:一点点搜索在JSR-308中发现了以下内容,他们争辩说在这种情况下允许空值:
我们注意到对该提案可能存在的反对意见。
该提案无法解决以前无法实现的任何事情。
与null相比,程序员定义的特殊值提供了更好的文档记录,这可能意味着“无”,“未初始化”,null本身等。
该建议更容易出错。忘记对null进行检查要比忘记对显式值进行检查要容易得多。
该提议可以使标准习语更加冗长。当前,仅注释的用户需要检查其特殊值。有了这个提议,许多处理注释的工具将不得不检查字段的值是否为空,以免它们抛出空指针异常。
我认为只有最后两点与“为什么一开始就不这样做”有关。最后一点肯定会带来好处-注释处理器永远不必担心它们将在注释值上获得null。我倾向于看到,随着注释处理器和其他此类框架代码的工作越来越多,必须进行这种检查才能使开发人员的代码更清晰,而不是反过来,但这无疑会使更改它变得不合理。
试试这个
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeInterface {
Class bar() default void.class;
}
它不需要新的类,它已经是Java中的关键字,没有任何意义。
Class<? extends Foo> bar() default void.null
无法编译。
看起来这是非法的,尽管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语言规范不允许在注释默认值中使用null值。
我倾向于做的是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语言规范(此处)中定义的常量表达式。
而且,Java语言规范说
如果元素的类型与指定的默认值不相称(第9.7节),则是编译时错误。
如果元素类型与元素值不匹配,则是编译时错误。当且仅当以下条件之一为真时,元素类型T才与元素值V相称:
T
是数组类型E[]
,并且:
- [...]
T
不是数组类型,和类型V
被分配兼容(第5.2节)与T
,和:
- 如果
T
是基本类型或String
,V
则为常数表达式(第15.28节)。- 如果
T
是Class
或对Class
(§4.5)的调用,V
则为类文字(§15.8.2)。- 如果
T
为枚举类型(第8.9节),V
则为枚举常量(第8.9.1节)。- V不为null。
因此,此处的元素类型Class<? extends Foo>
与默认值不符,因为该值是null
。
or
和内部and
,这是不能忽略的。
正如其他人所说,有一条规则说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;
}
这样做的主要缺点(除了需要枚举可能的值之外)是您现在将值包装在枚举中,因此您需要在枚举上调用属性/获取程序以获取该值。
Class<? extends Foo> bar();
。