Effective Java 第 2 版:第 5 章 項目 23

ここから 5 章の「ジェネリックス」に入る。 ジェネリックスを適切に使えば、型安全なコードを書くことができる。

項目 23: 新たなコードで原型を使用しない

Java 1.5 より前に、コレクション宣言で用いられていた原型 (raw type) は、型安全の観点から、もはや使うべきではないという話。

原型とは

原型とは、List<String> list; と宣言したときの List の部分。 要は型の名前のこと。

なぜ原型は使うべきでないか

原型で使うということは、List list = new ArrayList(); と宣言するということである。 つまり、型パラメータがない。 型パラメータがないと、どんな型の要素もコレクションに入れることができてしまう。 このとき、以下のように、キャストした型と違う型が入っていたら実行時エラーになり、ClassCastException が投げられる。

list = Arrays.asList("123", 456, "789");    // 2 個目でうっかり
String str = (String)list.get(1);    // ClassCastException 発生

よって、原型は使うべきでない。 原型は、互換性を保つために残しているだけである。

よりよい方法:パラメータ化された型を使う

パラメータ化された型とは、List<String> のように、<, > で囲まれたパラメータが指定された型のことである。 List<String> 型のコレクションは、String 型のオブジェクトしか要素として許可しない。

List<String> list = new ArrayList<String>();

として宣言すれば、先ほどのコードはコンパイル時にエラーとなる。 これは、実行時にエラーで落ちるより安全である。

メソッドの引数などにおいて、どんな型パラメータのコレクションが渡されてくるかわからないときがある。 そのようなときは、例えば Collection<?> を使う。 型パラメータが ? で指定されるとき、この型は非境界ワイルドカードと呼ばれる。 非境界ワイルドカード型はどんなコレクションでも受け取れる。 例えば、

static int numElementsInCommon(Set<?> s1, Set<?> s2);

のようにできる。

ここで、s1, s2 は、どんな型の要素でも入れられてしまうコレクションなのではないかという懸念が出てくる。 しかし、この場合も問題はない。 なぜなら、非境界ワイルドカードCollection<?> のコレクションには、null 以外のどんな要素も入れられないからである。 誤った型のオブジェクトを入れることがないので、実行時エラーを防ぐことができる。

例外的な場合

クラスリテラルとして使うとき、また、instanceof に対しては、原型を用いる。

参考文献

EFFECTIVE JAVA 第2版 (The Java Series)

EFFECTIVE JAVA 第2版 (The Java Series)