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の実装が参考になるはずだ。