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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1178104/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 00:33:21  来源:igfitidea点击:

Error setting a default null value for an annotation's field

javaannotationsdefault

提问by ripper234

Why am I getting an error "Attribute value must be constant". Isn't nullconstant???

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

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

采纳答案by Yishai

I don't know why, but the JLSis very clear:

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

 Discussion

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

And the definition of a default element is:

默认元素的定义是:

     DefaultValue:
         default ElementValue

Unfortunately I keep finding that the new language features (Enums and now Annotations) have very unhelpful compiler error messages when you don't meet the language spec.

不幸的是,我一直发现当你不符合语言规范时,新的语言特性(枚举和现在的注释)有非常无用的编译器错误消息。

EDIT: A little googleing found the followingin the JSR-308, where they argue for allowing nulls in this situation:

编辑:有一点googleing发现下面的JSR-308,在那里他们认为允许在这种情况下,空值:

We note some possible objections to the proposal.

The proposal doesn't make anything possible that was not possible before.

The programmer-defined special value provides better documentation than null, which might mean “none”, “uninitialized”, null itself, etc.

The proposal is more error-prone. It's much easier to forget checking against null than to forget checking for an explicit value.

The proposal may make the standard idiom more verbose. Currently only the users of an annotation need to check for its special values. With the proposal, many tools that process annotations will have to check whether a field's value is null lest they throw a null pointer exception.

我们注意到该提议可能存在一些反对意见。

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

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

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

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

I think only the last two points are relevant to "why not do it in the first place." The last point certainly brings up a good point - an annotation processor never has to be concerned that they will get a null on an annotation value. I tend to see that as more the job of annotation processors and other such framework code to have to do that kind of check to make the developers code clearer rather than the other way around, but it would certainly make it hard to justify changing it.

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

回答by skaffman

It would seem this is illegal, although the JLS is very fuzzy on this.

这似乎是非法的,尽管 JLS 对此非常模糊。

I wracked my memory to try and think of an existing annotation out there that had a Class attribute to an annotation, and remembered this one from the JAXB API:

我绞尽脑汁去想一个现有的注释,它有一个注释的 Class 属性,并从 JAXB API 中记住了这个:

@Retention(RUNTIME) @Target({PACKAGE,FIELD,METHOD,TYPE,PARAMETER})        
public @interface XmlJavaTypeAdapter {
    Class type() default DEFAULT.class;

    static final class DEFAULT {}    
}

You can see how they've had to define a dummy static class to hold the equivalent of a null.

您可以看到他们如何定义一个虚拟静态类来保存与 null 的等效项。

Unpleasant.

不愉快。

回答by Logan Murphy

Try this

尝试这个

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

It does not require a new class and it is already a keyword in Java that means nothing.

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

回答by Shervin Asgari

It seem there is one other way of doing it.

似乎还有另一种方法可以做到这一点。

I don't like this either, but it might work.

我也不喜欢这个,但它可能有用。

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

So basically you create an empty Array instead. This seems to allow a default value.

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

回答by Sotirios Delimanolis

That error message is misleading, but, no, the nullliteral is not a constant expression, as defined in the Java Language Specification, here.

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

Moreover, the Java Language Specificationsays

此外,Java 语言规范

It is a compile-time error if the type of the element is not commensurate(§9.7) with the default value specified.

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

And to explain what that means

解释这意味着什么

It is a compile-time error if the element type is not commensurate with the element value. An element type T is commensurate with an element value Vif and only if one of the following is true:

  • Tis an array type E[], and either:
    • [...]
  • Tis not an array type, and the type of Vis assignment compatible (§5.2) with T, and:
    • If Tis a primitive type or String, then Vis a constant expression (§15.28).
    • If Tis Classor an invocation of Class(§4.5), then Vis a class literal (§15.8.2).
    • If Tis an enum type (§8.9), then Vis an enum constant (§8.9.1).
    • V is not null.

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

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

So here, your element type, Class<? extends Foo>, is not commensurate with the default value, because that value is null.

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

回答by Gray

Class<? extends Foo> bar() default null;// this doesn't compile
Class<? extends Foo> bar() default null;// this doesn't compile

As mentioned, the Java language specification does not allow null values in the annotations defaults.

如前所述,Java 语言规范不允许在注释默认值中使用空值。

What I tend to do is to do is to define DEFAULT_VALUEconstants at the top of the annotation definition. Something like:

我倾向于做的是DEFAULT_VALUE在注释定义的顶部定义常量。就像是:

public @interface MyAnnotation {
    public static final String DEFAULT_PREFIX = "__class-name-here__ default";
    ...
    public String prefix() default DEFAULT_PREFIX;
}

Then in my code I do something like:

然后在我的代码中,我执行以下操作:

if (myAnnotation.prefix().equals(MyAnnotation.DEFAULT_PREFIX)) { ... }

In your case with a class, I would just define a marker class. Unfortunately you can't have a constant for this so instead you need to do something like:

在您使用类的情况下,我只会定义一个标记类。不幸的是,您不能为此设置常量,因此您需要执行以下操作:

public @interface MyAnnotation {
    public static Class<? extends Foo> DEFAULT_BAR = DefaultBar.class;
    ...
    Class<? extends Foo> bar() default DEFAULT_BAR;
}

Your DefaultFooclass would just an empty implementation so that the code can do:

您的DefaultFoo类只是一个空实现,以便代码可以执行以下操作:

if (myAnnotation.bar() == MyAnnotation.DEFAULT_BAR) { ... }

Hope this helps.

希望这可以帮助。

回答by AMTerp

As others have said, there is a rule which says that null is not a valid default value in Java.

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

If you've got a limited number of possible values that the field can have, then one option for working around this is to make the field type a custom enum which itself contains a mapping to null. For example, imagine I have the following annotation that I want a default null for:

如果字段可以具有的可能值数量有限,那么解决此问题的一种选择是使字段类型成为自定义枚举,该枚举本身包含到 null 的映射。例如,假设我有以下注释,我想要一个默认的空值:

public @interface MyAnnotation {
   Class<?> value() default null;
}

As we've established, this is not allowed. What we can instead do is define an enum:

正如我们已经确定的那样,这是不允许的。我们可以做的是定义一个枚举:

public enum MyClassEnum {
    NULL(null),
    MY_CLASS1(MyClass1.class),
    MY_CLASS2(MyClass2.class),
    ;

    public final Class<?> myClass;

    MyClassEnum(Class<?> aClass) {
        myClass = aClass;
    }
}

and change the annotation as such:

并将注释更改为:

public @interface MyAnnotation {
  MyClassEnum value() default MyClassEnum.NULL;
}

The main downside of this (asides from needing to enumerate your possible values) is that you've now wrapped your value in an enum, so you'll need to invoke the property/getter on the enum to get the value.

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