86

为什么我收到错误“属性值必须是常量”。不是null常量吗???

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeInterface {
    Class<? extends Foo> bar() default null;// this doesn't compile
}
4

7 回答 7

69

我不知道为什么,但是JLS很清楚:

 Discussion

 Note that null is not a legal element value for any element type. 

默认元素的定义是:

     DefaultValue:
         default ElementValue

不幸的是,当您不符合语言规范时,我不断发现新的语言功能(枚举和现在的注释)具有非常无用的编译器错误消息。

编辑:JSR-308 中进行了一些谷歌搜索,他们主张在这种情况下允许空值:

我们注意到对该提案的一些可能的反对意见。

该提案没有使以前不可能的任何事情成为可能。

程序员定义的特殊值提供了比 null 更好的文档,这可能意味着“无”、“未初始化”、null 本身等。

该提议更容易出错。忘记检查 null 比忘记检查显式值要容易得多。

该提案可能会使标准习语更加冗长。目前只有注释的用户需要检查它的特殊值。根据该提议,许多处理注释的工具将不得不检查字段的值是否为空,以免引发空指针异常。

我认为只有最后两点与“为什么不首先做”有关。最后一点当然提出了一个很好的观点——注释处理器永远不必担心它们会在注释值上得到空值。我倾向于认为注释处理器和其他此类框架代码的工作更多是必须进行这种检查以使开发人员的代码更清晰,而不是相反,但这肯定会很难证明更改它的合理性。

于 2009-07-24T15:03:03.473 回答
69

试试这个

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeInterface {
    Class bar() default void.class;
}

它不需要新的类,它已经是 Java 中的一个关键字,没有任何意义。

于 2013-04-24T19:43:58.937 回答
55

这似乎是非法的,尽管 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 的值。

不愉快。

于 2009-07-24T14:43:58.917 回答
11

似乎还有另一种方法。

我也不喜欢这个,但它可能会起作用。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeInterface {
    Class<? extends Foo>[] bar() default {};
}

所以基本上你创建一个空数组。这似乎允许使用默认值。

于 2014-09-02T13:02:22.187 回答
3
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) { ... }

希望这可以帮助。

于 2017-07-07T15:42:54.390 回答
2

正如其他人所说,有一条规则说 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 来获取值。

于 2019-11-07T04:57:52.543 回答
1

该错误消息具有误导性,但是,不,null字面量不是 Java 语言规范中定义的常量表达式,here

此外,Java语言规范

如果元素的类型与指定的默认值不相称(第 9.7 节),则会出现编译时错误。

解释这意味着什么

如果元素类型与元素值不相称,则为编译时错误。当且仅当以下条件之一为真时,元素类型 T 与元素值 V 相称:

  • T是一个数组类型E[],并且:
    • [...]
  • T不是数组类型,并且 的类型与 , 和 的赋值兼容V(第 5.2 节): T
    • 如果T是原始类型或String,则V是常量表达式(第 15.28 节)。
    • 如果TClass或调用Class(§4.5),则V是类文字(§15.8.2)。
    • 如果T是枚举类型(第 8.9 节),V则为枚举常量(第 8.9.1 节)。
    • V 不为空

所以在这里,您的元素类型 ,Class<? extends Foo>与默认值不相称,因为该值是null.

于 2016-12-24T01:02:20.880 回答