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

时间:2009-07-24 14:35:41

标签: java annotations default

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

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

7 个答案:

答案 0 :(得分:57)

我不知道为什么,但JLS非常明确:

 Discussion

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

默认元素的定义是:

     DefaultValue:
         default ElementValue

不幸的是,当你不符合语言规范时,我一直发现新的语言功能(Enums和now Annotations)有非常无用的编译错误信息。

编辑:有点googleing在JSR-308中发现了following,他们认为在这种情况下允许空值:

  

我们注意到该提案可能存在一些异议。

     

该提案没有做出任何以前无法做到的事情。

     

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

     

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

     

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

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

答案 1 :(得分:56)

试试这个

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

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

答案 2 :(得分:48)

虽然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。

不愉快。

答案 3 :(得分:9)

似乎还有另外一种方法。

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

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

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

答案 4 :(得分:2)

$question->answers()->count()

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

我倾向于做的是在注释定义的顶部定义Class<? extends Foo> bar() default null;// this doesn't compile 常量。类似的东西:

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

希望这有帮助。

答案 5 :(得分:1)

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

此外,Java Language Specification

  

如果元素的类型不相称(§9.7)且指定了默认值,则为编译时错误。

explain what that means

  

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

     
      
  • T是数组类型E[],并且:      
        
    • [...]
    •   
  •   
  • T不是数组类型V的类型与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

答案 6 :(得分:0)

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

如果字段只能包含有限数量的可能值,那么解决此问题的一种方法是使字段类型为自定义枚举,该枚举本身包含一个对null的映射。例如,假设我有以下注释,我希望将其设置为默认的null:

// getting value from textbox
var data = document.getElementById('youtextBoxId').value;

// data = [{"id":"15aea3fa","firstname":"John","lastname":"Doe"}]

// convert it to json format
data = JSON.parse(data);

// getting Id of first data
alert(data[0]['id']); //15aea3fa

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

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;
    }
}

此方法的主要缺点(除了需要枚举可能的值外)是您现在将值包装在枚举中,因此您需要在枚举上调用属性/获取程序以获取该值。