総称クラスを使ってみる
Java5.0におけるJava言語拡張の筆頭はおそらく総称クラスだろうか...なんか用語の使用方法にあんまり自信が持てないがとにかく試してみた.
今までは例えばArrayListはObjectを要素として格納するコレクションであった.Objectを要素とするということは,参照型なら何でも格納できるので,汎用性がある.その半面何でも格納できてしまうので,使用する側はキャストしたりする.そして間違えてもコンパイラでは検出できず,実行して始めてClassCastExceptionになって気がつくことになる.汎用性を確保した半面,使用方法の正しさはプログラマが保証する必要があった.あるいは,ある程度コンパイラで誤りを検出するために,プログラマは特定の型のオブジェクトを要素とするサブクラスを作成するパターンもあったように思う.総称型はこのような問題に対応する.JDK5.0でのArrayListは以下のようにnewできる.
List<String> list = new ArrayList<String>();これで,Stringを要素に持つListを作成することができる.このリストにString以外のものを要素として追加使用とすると,コンパイル時にエラーとなる.
総称クラスを使用するサンプルを以下に示す.
ArrayListのAPIリファレンスを参照すると,
public class ArrayList<E>という記述がある.すなわちArrayListは総称クラスで,このクラスではEという識別子がなんらかの意味を持つ.で,APIリファレンスをさらに読み進めると,ArrayListは
void add(E o)というメソッドを持つことになっている.Eとは,先程のEでなんらかの型を示す.で,何らかの型というのは,すなわちコーディングの段階で決まる.サンプルでは
List<String> list = new ArrayList<String>();と記述した.すなわちEはStringで,
void add(E o)は
void add(String o)となる.したがってlist#addにはStringかその子孫のクラス以外を引数に渡すとコンパイルエラーとなる.さらにArrayListは
Iterator<E> iterator()というメソッドを持つことになっている.上の続きだとEはStringなので,
Iterator<String> iterator()である.これはサンプルでは
Iteratorの部分である.Iteratorも総称クラスで,itr = list.iterator();
E next()というメソッドを持つ.サンプルのitrはやはりEをStringとしているので,
String next()したがって,サンプルでは
String str = itr.next();とキャストなしでitr.next()の値をstrに代入している.これは今までなら
String str = (String)itr.next();
としていたはずである.
もう一つ,総称クラスを使用するサンプルを以下に示す.
総称型においても,拡張の概念がある.これは総称クラスを具体的なクラスに置き換えてしまえば,あとは従来通りと同じ考えを適用できる.
List<Hoge> hogeList = new ArrayList<Hoge>(); List<Fuga> fugaList = new ArrayList<Fuga>(); // add(E o) を踏まえて hogeList.add(new Hoge()); hogeList.add(new Fuga()); // 誤り // fugaList.add(new Hoge()); fugaList.add(new Fuga());Fuga extends Hogeであるので,hogeList#addにはFugaインスタンスを引数に渡してもコンパイルエラーにはならない.だが,逆にfugaList#addにHogeインスタンスを渡すとコンパイルエラーとなる.さらにちょっとややこしいが,ArrayListには以下のメソッドがある.
addAll(Collection<? extends E> c)なんだかすごい記述であるが,addAllはEを拡張した何かを要素に持つコレクションを引数にもつということである.
// addAll(Collection<? extends E> c) を踏まえて hogeList.addAll(hogeList); hogeList.addAll(fugaList); // 誤り // fugaList.addAll(hogeList); fugaList.addAll(fugaList);Fuga extends Hogeであるので,hogeList#addAllにはList<Fuga>を引数に渡してもコンパイルエラーにはならない.だが,逆にfugaList#addAllにhogeListを渡すとコンパイルエラーとなる.
GenericClassSample2.java:20: シンボルを見つけられません。
シンボル: メソッド addAll(java.util.List<Hoge>)
場所 : java.util.List<Fuga> の インタフェース
fugaList.addAll(hogeList);
総称型では明確に
<? extends E>と拡張を含むかどうかを記述しないといけないことに注意.サンプルの以下の部分.
// Iterator<E> iterator() を踏まえて Iterator<Hoge> hogeItr = hogeList.iterator(); // 誤り // hogeItr = fugaList.iterator(); Iterator<Fuga> fugaItr = fugaList.iterator(); // 誤り // fugaItr = hogeList.iterator();ここで以下の誤りのコメントアウトを解除するとコンパイルエラーとなる.
// 誤り // hogeItr = fugaList.iterator();は誤りである.
GenericClassSample2.java:32: 互換性のない型
検出値 : java.util.Iterator<Fuga>
期待値 : java.util.Iterator<Hoge>
hogeItr = fugaList.iterator();

