[Java] 警告抑制はコンパイルオプションじゃなくAnnotationでやるべき

J2SE1.5ではGeneric Typeが使えるようになったけど、
このために型チェックでキャストなどがだいぶ厳しくなっています。

たとえば、次のようなコードは-source 1.5では通常エラーとなります。

 C create(Class keyClass) throws Exception {
  Class subClass = oldlib.generateSubType(keyClass);
  return (C) subClass.newInstance(); // このキャストで警告またはエラー
}

oldlib.generateSubType(Class)は引数のクラスのサブクラスを見つけたり生成したりして、それをClassオブジェクトとして返すというものだとします。GenericType(ここではのこと)の場合、そのキャストが型システム的に整合性が取れて無いとコンパイルエラーにします。ここでは、subClass.newInstance()の戻り値はObject型であり、それがCまたはCの派生クラスであるという保障が無い、ということです*1

これをエラーにしない方法があります。javacのオプションで-Xlint:uncheckedを指定することです。こうするとエラーではなく警告を出すにとどまります。

問題はこのオプションがコンパイラ全体で有効になるため、これが出す警告が、書いた人が「違反になるのは理解しているけど、(過去のライブラリ利用やランタイム拡張等で)やらなくてはいけない場合」と、「うっかり違反したコードを入れてしまった場合」とを区別しないということです。uncheckedの警告が多くなると、うっかり違反のほうも気がつかなくなってしまうかもしれません。

そこで、J2SE1.5で入ったAnnotationで制御できるとうれしいと思います。Annotationはpublic とかnative とかfinalとかのように、コンパイラやランタイムが認識する言語構成要素に対するメタデータ設定をユーザーが独自に追加定義・解釈できるようになる機能。たとえば、

package java.lang;

import java.lang.annotation.*;

@Retention(RetentionPolicy.SOURCE)
@Target(new ElementType[]{ElementType.METHOD, ElementType.METHOD})
public @interface Unsafe {
   UnsafeType value();
}

public enum UnsafeType {
  CAST, ...
}

というようにannotationが定義できて、

@Unsafe(UnsafeType.CAST)
 C create(Class keyClass) throws Exception {
  Class subClass = oldlib.generateSubType(keyClass);
  return (C) subClass.newInstance(); 
}

という風に使えます。
このようになってたらコンパイラがこのメソッドの中のキャストに関する型チェック警告は抑制する、というようなコンパイラ実装も書くことができるわけです。

こういうコンパイラになっていることで、警告があることをわかってる人はannotationをつければよく、それでも警告が起こるのは、うっかりミスだけというようになると思います。

同様に@UseDeprecatedなど、問題がわかっていても使わざろうえないところは、annotationでコンパイラ制御できるようになれば良いと思います。こういったものの制御はコンパイラのオプションではなく、より細かい単位で制御できるannotationを使って行うのが適切ではないか、と思うしだいです。

*1:ClassのnewInstance()メソッドの戻り値はC型になります