web-dev-qa-db-ja.com

注釈のフィールドにデフォルトのヌル値を設定する際のエラー

「属性値は一定でなければならない」というエラーが表示されるのはなぜですか。 null定数ではない???

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

理由はわかりませんが、 [〜#〜] jls [〜#〜] は非常に明確です。

 Discussion

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

デフォルトの要素の定義は次のとおりです。

     DefaultValue:
         default ElementValue

残念ながら、新しい言語機能(Enumと現在の注釈)には、言語仕様を満たしていない場合、非常に役に立たないコンパイラエラーメッセージがあることがわかります。

編集:少しグーグルは、JSR-308で following を見つけました。そこでは、この状況でnullを許可することを主張しています:

提案に対するいくつかの考えられる異議に留意する。

この提案は、以前は不可能であったことを何も可能にしません。

プログラマーが定義した特別な値は、「なし」、「初期化されていない」、ヌル自体などを意味する可能性があるヌルよりも優れたドキュメントを提供します。

提案はよりエラーが発生しやすいです。明示的な値のチェックを忘れるよりも、nullに対するチェックを忘れる方がはるかに簡単です。

この提案により、標準的なイディオムがより冗長になる場合があります。現在、注釈のユーザーのみがその特別な値を確認する必要があります。提案では、アノテーションを処理する多くのツールは、フィールドの値がnullポインター例外をスローしないように、nullかどうかをチェックする必要があります。

最後の2つのポイントのみが「最初にそれをしない理由」に関係があると思います。最後のポイントは確かに良いポイントをもたらします-アノテーションプロセッサは、アノテーション値でnullを取得することを心配する必要はありません。アノテーションプロセッサやその他のフレームワークコードの仕事が開発者のコ​​ードを他の方法よりも明確にするためにこの種のチェックを行う必要があるほど、それを変更することを正当化することは確かに難しくなるでしょう。

57
Yishai

これを試して

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

新しいクラスは必要なく、すでにJavaのキーワードです。

56
Logan Murphy

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に相当するものを保持するためにダミーの静的クラスを定義しなければならなかったことがわかります。

不快。

49
skaffman

別の方法があります。

私もこれは好きではありませんが、うまくいくかもしれません。

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

したがって、基本的には代わりに空の配列を作成します。これはデフォルト値を許可するようです。

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

お役に立てれば。

2
Gray

このエラーメッセージは誤解を招くものですが、いいえ、nullリテラルは、Java Language Specification、 here で定義されている定数式ではありません。

さらに、 Java言語仕様

要素のタイプが指定されたデフォルト値と一致しない場合(§9.7)は、コンパイル時エラーです。

そして その意味を説明する

要素タイプが要素値と一致しない場合、コンパイル時エラーです。 要素タイプTは要素値Vと同等です。次のいずれかが当てはまる場合に限ります。

  • Tは配列型E[]、および次のいずれか:
    • [...]
  • Tは配列型ではありません。また、Vの型はTand:[と互換性のある割り当て(§5.2)です。 ____。]
    • Tがプリミティブ型またはStringである場合、Vは定数式です(§15.28)。
    • TClassまたはClassの呼び出し(§4.5)の場合、Vはクラスリテラル(§15.8.2)です。
    • Tが列挙型(§8.9)の場合、Vは列挙型定数(§8.9.1)です。
    • Vはnullではありません

したがって、ここでは、要素タイプClass<? extends Foo>は、その値がnullであるため、デフォルト値と釣り合っていません。