ChatGPT解决这个技术问题 Extra ChatGPT

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

为什么我收到错误“属性值必须是常量”。不是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",但它仍然不是常量表达式,因此是不允许的。

H
Holger

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

 Discussion

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

默认元素的定义是:

     DefaultValue:
         default ElementValue

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

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

我们注意到对该提案的一些可能的反对意见。该提案没有使以前不可能的任何事情成为可能。程序员定义的特殊值提供了比 null 更好的文档,这可能意味着“无”、“未初始化”、null 本身等。该提议更容易出错。忘记检查 null 比忘记检查显式值要容易得多。该提案可能会使标准习语更加冗长。目前只有注释的用户需要检查它的特殊值。根据该提议,许多处理注释的工具将不得不检查字段的值是否为空,以免引发空指针异常。

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


B
Brad Cupit

尝试这个

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

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


我喜欢。唯一的问题——在原始问题的上下文中——是这个答案不支持类型参数:Class<? extends Foo> bar() default void.null 不编译。
void.class 一般不能应用: public @interface NoNullDefault { String value() default void.class; }
对于 Groovy,即使在评论中提到的情况下,它也可以正常工作
大声笑:这意味着什么与那意味着“什么都没有”
s
skaffman

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

不愉快。


是的,我们做了类似的事情(我们只是使用 Foo.class 作为默认值)。糟透了。
对于字符串字段,您必须求助于 magic strings。显然,众所周知的反模式比现在广为人知的语言特性要好。
@TimYates 当 null 可用并被普遍理解时,看到人们定义自己的“无价值”哨兵值,这让我很生气。现在这是语言本身所要求的?!幸运的是,您可以将“魔术字符串”定义为公共常量。它仍然不理想,但至少正在寻找注释的代码可以引用常量,而不是在任何地方使用文字。
S
Shervin Asgari

似乎还有另一种方法。

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

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

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


G
Gray

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

希望这可以帮助。


顺便说一句,我只是用一个字符串尝试了这个,但这不起作用。您不会返回相同的 String 对象。
您使用的是 .equals() 而不是 == @Doradus?您是否获得了不同的 或不同的对象。
不同的对象。我使用了 ==
好吧,这个班级是一个单身人士@Doradus。我本来希望字符串也是单例的,但我想它不是,您需要使用 .equals()。奇怪。
A
Alexander Terp

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


S
Sotirios Delimanolis

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

此外,Java Language Specification

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

explain what that means

如果元素类型与元素值不相称,则为编译时错误。当且仅当以下条件之一为真时,元素类型 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


您的解决方案不适合复制和粘贴;-) 有些人在阅读时不喜欢思考
恕我直言,是的,你做到了(但不是我)。您的表达式读作“when (...) or (V is not null)”,听起来任何非空值都是正确的。 JLS 公式是一个真正的野兽,但有一个外部 or 和一个内部 and,不能省略。
@maaartinus 很久以前没有看到您的评论,更新了我的答案以添加整个报价。