JSR 308: Annotations on Java Types

J2SE 5.0から導入されたアノテーション(JSR 175: A Metadata Facility for the Java Programming Language)はJavaによるプログラミングスタイルを大きく変化させ、とくにJava EEアプリケーション開発の分野ではすでに不可欠なものになりつつある。その後も「JSR 250: Common Annotations for the Java Platform」などによって利用できるアノテーションの種類が充実してきたほか、「JSR 269: Pluggable Annotation Processing API」のJava SE 6への導入によってアノテーションの処理を比較的容易に行えるようになった。

Java SE 7ではそのアノテーションの機能をさらに拡張する「JSR 308: Annotations on Java Types」の導入が検討されている。現在のアノテーションはクラス、メソッド、フィールドそして変数の宣言に対して指定することができる。JSR 308はその範囲をさらに拡大し、配列やメソッドレシーバ、ジェネリックの型引数などにも指定できるようにする。

JSR 308の仕様は現在議論されている最中だが、最新のProposalがこのページで公開されている。それによれば、JSR 308ではとくに型に対するアノテーションの適用を可能にすることが主題となっており、その他の検討事項としてはローカル変数や配列、文に対する適用が挙げられている。一方、式やブロック、ループ、部分構造などに対しては適用しないという。

また、ProposalではJSR 308を適用できる場面としてリスト1に示すような例が挙げられている。

リスト1 JSR 308によるアノテーションの適用例

// ジェネリックにおける型引数
Map> files;

// ジェネリックにおけるメソッドやコンストラクタ呼び出しの引数
o.m("...");

// 型引数の境界指定
class Folder { ... }
Collection super @Existing File>

// クラスの継承
class UnmodifiableList implements @Readonly List { ... }

// 例外指定
void monitorTemperature() throws @Critical TemperatureException { ... }

// 型キャスト
myString = (@NonNull String) myObject;

// instanceofによる型チェック
boolean isNonNull = myString instanceof @NonNull String;

// オブジェクト生成
new @NonEmpty @Readonly List(myNonEmptyStringSet)
new  @Interned MyObject()

// メソッドレシーバ
public int size() @Readonly { ... }
public void write() @Writable throws IOException { ... }

// クラスリテラル
Class c = @NonNull String.class;

// 配列
Document[@Readonly][] docs4 = new Document[@Readonly 2][12];
Document[][@Readonly] docs5 = new Document[2][@Readonly 12];

たとえば最初のジェネリックスの例ならば、mapに格納するデータは空でないList、キーはnull以外でなければならないことを示している。また、Listの中身は読込専用のDocumentでなければならない。ただし、ここで使われている@NonNullや@NonEmptyなどのアノテーションは架空のもので、実際にこのようなアノテーションがJava SEに用意されるということではない。

JSR 308対応のコンパイラを試す

JSR 308に対応したコンパイラは、開発版をここよりダウンロードすることができる。これはOpenJDKプロジェクトによって公開されているコンパイラを、JSR 308仕様に合わせて改変したものだ。

ファイル「jsr308-compiler.zip」をダウンロードして適当な場所で展開すると、中にはcompilerディレクトリ(以下、これを[COMPILER_DIR]と表記)が納められている。ビルドするにはApache Antを用いてプロンプト1のようにコマンドを実行する。

プロンプト1 JSR 308用コンパイラのビルド

> cd [COMPILER_DIR]
> ant build

あるいは、NetBeansプロジェクト形式になっているのでNetBeansを用いてビルドしてもよい。ビルドに成功するとdistディレクトリにbinとlibという2つのディレクトリが生成される。libディレクトリにはコンパイラの本体となるjavac.jarが、binディレクトリにはjavac.jarを"java"コマンドとして使用するためのjava.shが配置される。

なお、jsr308-compiler.zipはコンパイラのみなので新しいアノテーションは含まれていない。現在のところ、同じサイトで公開されている「Type-checking Plug-ins(jsr308-checkers.zip)」によってテスト用の@NonNullアノテーションのみ利用可能となっている。@NonNullは、型変数に対してnullの代入を許容しないようにするアノテーションである。Type-checking Plug-insはJSR 308のコンパイラをテストするためのプログラム群で、@NonNullアノテーションはそのサンプルとして公開されているのだが、今後使用できるアノテーションの種類を増やしていくとのことである。

jsr308-checkers.zipを適当な場所で展開すると、中にはcheckersディレクトリ(以下、これを[CHECKERS_DIR]と表記)が納められている。ビルドするにはApache Antを用いてプロンプト2のようにコマンドを実行する。

プロンプト2 Type-checking Plug-insのビルド

> cd [CHECKERS_DIR]
> ant dist

ビルドに成功するとcheckers.jarというファイルが生成される。このJARファイルに、@NonNullアノテーションを処理するためのアノテーションプロセッサが含まれている。

続いて、@NonNullアノテーションを用いたプログラムの例をリスト2に示す。ここでは文字列strおよびジェネリックスを用いたMapの型引数に@NonNullを指定している。

リスト2 @NonNullアノテーションを使用したプログラム例

import checkers.quals.NonNull;
import java.util.HashMap;
import java.util.Map;

class NonNullTest {
    public static void main(String[] args) {
        @NonNull String str = "Hello";

        Map map = new HashMap();
        map.put(str, "World");
    }
}

このプログラムをコンパイルするわけだが、JSR 308のコンパイラには新たに-typeprocessorというオプションが追加されている。これは-processorオプションに似たもので、JSR 308のアノテーション処理を行うプロセッサを指定するために使用する。たとえばリスト2のコンパイルはプロンプト3のように行う。現時点でJSR 308対応のコンパイラ自体がjavac.jarとして提供されているため、javaコマンドでjavac.jarを実行する形式となっている。シェルが利用可能な環境では、コンパイラのbinディレクトリにあるjava.shを用いればこれを疑似的にjavaコマンドとして実行できるようになる。

プロンプト3 -typeprocessorオプションを指定してコンパイル

> java -jar javac.jar -classpath [CHECKERS_JAR_PATH] -typeprocessor checkers.nonnull.NonnullChecker NonNullTest.java

リスト2は正常にコンパイルできるが、これをリスト3やリスト4のように、@NonNullを指定した部分にnullを代入しようとすると、アノテーションを処理する際にエラーが発生するようになる(ただし、現時点ではType-checking Plug-insのバグにより通常のエラーではなく例外は発生してしまう)。

リスト3 @NonNullによってエラーが発生するケース

        @NonNull String str = null;

リスト4 @NonNullによってエラーが発生するケース その2

        String str = null;
        Map map = new HashMap();
        map.put(str, "World");

JSR 308のproposalは日々の議論に応じて逐次更新されており、コンパイラもそれに対応する形で修正されている。前述したようにType-checking Plug-insにも今後テストできるアノテーションが追加される予定である。また、興味のある人は自分でJSR 308に対応したアノテーションを自作してみるのもいいだろう。その際には@NonNullの実装が参考になるはずだ。