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

wqlqzqxt  于 2023-01-11  发布在  Java
关注(0)|答案(7)|浏览(142)

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

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

vmdwslir1#

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

Discussion

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

默认元素的定义为:

DefaultValue:
         default ElementValue

不幸的是,我不断发现新的语言特性(Enums和现在的Annotations)在您不满足语言规范时会显示非常无用的编译器错误消息。
编辑:稍微搜索一下JSR-308,发现了以下内容,他们主张在这种情况下允许空值:
我们注意到对这项建议可能有一些反对意见。
这项提议没有使以前不可能的事情成为可能。
程序员定义的特殊值提供了比null更好的文档,null可能意味着“无”、“未初始化”、null本身等等。
这个提议更容易出错,忘记检查null比忘记检查显式值要容易得多。
这个提议可能会使标准习惯用法变得更加冗长。目前只有注解的用户需要检查它的特殊值。根据这个提议,许多处理注解的工具将不得不检查字段的值是否为空,以免抛出空指针异常。
我认为只有最后两点与“为什么不在第一个地方做它”相关。最后一点当然提出了一个很好的观点-注解处理器永远不必担心它们会在注解值上得到空值。我倾向于认为,注解处理器和其他类似框架代码的工作更多地是必须进行这种检查,以使开发人员的代码更清晰,而不是相反,但这肯定会使改变它变得很难。

zd287kbt

zd287kbt2#

试试这个

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

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

mlnl4t2r

mlnl4t2r3#

这似乎是非法的,虽然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的等价物的。
不愉快。

fquxozlt

fquxozlt4#

似乎还有另一种方法可以做这件事。
我也不喜欢这样,但可能有用。

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

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

13z8s7eq

13z8s7eq5#

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

希望这个有用。

qqrboqgw

qqrboqgw6#

正如其他人所说的,有一条规则规定null不是Java中有效的默认值。

如果字段的可能值数量有限,那么解决这个问题的一个方法是将字段类型设置为自定义枚举,该枚举本身包含到null的Map。例如,假设我有以下注解,我希望为它设置默认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;
}

这样做的主要缺点(除了需要枚举可能的值之外)是,您现在已经将值 Package 在一个枚举中,因此您需要调用枚举上的属性/getter来获取值。

k4emjkb1

k4emjkb17#

不,Java语言规范没有将null文字/值定义为常量表达式。
在定义注解类型元素的可能默认值时,规范规定了以下内容:
如果元素的类型与指定的默认值不相称(参见9.7节),这是一个编译时错误。
为了解释这意味着什么
如果元素类型与元素值不相称,则为编译时错误。当且仅当下列条件之一为真时,元素类型T与元素值V相称:

  • T是数组类型E[],并且:
  • [...]
      • T不是数组类型**,并且V的类型与T是赋值兼容的(参见5.2节):
  • 如果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

相关问题