2009년 10월 12일 월요일

generic java

これまで3回にわたり、J2SE 5.0で導入されたGeneric Javaについて紹介し、Generic Javaを適用して作成されたAPIを使う方法をとりあげてきた。今回からは、Generic Javaを使ってAPIを作成する方法について紹介する。

ソースコードを読む

Generic Javaプログラミングを学ぶよい方法のひとつは、コレクションフレームワーク(java.util)のソースコードを読むことにある。コレクションフレームワークのソースコードで使われているGenericは生きたGenericだ。ためしにjava.util.ArrayListのソースコードを眺めてみるとよいだろう。

これはGenericに限らず多くの場合にいえることだが、実際に使われてるソースコードから学ぶのは有効な方法だ。そこには必要であるという観点に基づいた実装がある。教材では、その機能を説明する観点からしばしば、言語規則をベースにして無理やり説明のために起したソースコードが用いられるが、これは役に立たないことが多い。使いもしない枝葉末節な規則や実装方法を見せられて、その複雑さにうんざりしてプログラミングの道から遠ざかってしまった人も多いだろう。

実際に使われているソースコードではそういった危険性が少ない。実装はできるが実際にはあまり使わないといったソースコードを見る無駄を避けることができるし、機能以外でもっと大切なことが学べる。これからGeneric Javaの実装方法を紹介していくが、基本的な機能がわかったらコレクションフレームワーク(java.util)のソースコードを見てほしい。クラス、メソッド、インタフェース、継承、実装、そういったところでどのようにGenericを使うのかがよくわかるだろう。

Generic - メソッドで使う

実装の面から学ぶ場合、もっともわかりやすい例はメソッドでGenericを使う場合だろう。メソッドでGenericを使った実装例をリスト1に、そのメソッドを使う例をリスト2に示す。

リスト1 GenericMethod.java

public class GenericMethod {
    public <T> void doNothing(T aTarget) {
        //
    }
}

リスト2 Test1.java

public class Test1 {
    public static void main(String[] anyArguments) {
        GenericMethod gm = new GenericMethod();

        gm.doNothing(new Object());
        gm.doNothing(new Integer(1));
        gm.doNothing("String");
    }
}

まず、リスト1におけるリスト2.3に注目してほしい。publicのあとに<T>という表記が加わっている。これは、このメソッドで使用するTがテンプレートだということを意味している。名前はTでもHでもなんでもよいが、慣例的にはTやEといったように大文字一文字が使われることが多い。

J2SE 1.4以前であれば、(T aTarget)の部分でTというクラスはないというエラーが表示されたコンパイルできない。J2SE 5.0以降であれば<T>という指定からTはテンプレートとして扱えるから、(T aTarget)という指定が有効になる。この記述方法はメソッドでテンプレート使う場合のもっとも基本的な記述方法だから覚えてしまおう。

リスト3 メソッドでテンプレートを使う

    public <T> void doNothing(T aTarget)

リスト2からは、メソッドの引数にそれぞれ違うクラスのインスタンスを渡していることがわかる。J2SE 1.4以前であれば、引数の種類の分だけメソッドを用意するか、メソッド側でObjectとして受け取る必要があったが、その必要がなくなっていることがわかるだろう。

また、リスト1とリスト2をそれぞれ逆コンパイルしたものをリスト4およびリスト5に示しておく。内部ではObjectで受け取るような実装に変更されていることがわかる。

リスト4 GenericMethod.javaをコンパイルして逆コンパイルしたもの

public class GenericClass
{
    private Object element;
}

リスト5 Test1.javaコンパイルして逆コンパイルしたもの

public class Test1
{
    public static void main(String args[])
    {
        GenericMethod genericmethod = new GenericMethod();

        genericmethod.doNothing(new Object());
        genericmethod.doNothing(new Integer(1));
        genericmethod.doNothing("String");
    }
}

Generic - クラスで使う

今度はクラスでGenericを使う方法を紹介する。リスト6とリスト7が実装例だ。コレクションフレームワークでは主にリスト6とリスト7のようにクラスで使われている。

リスト6 GenericClass.java

public class GenericClass<E> {
    private E element;
}

リスト7 Test2.java

public class Test2 {
    public static void main(String[] anyArguments) {
        GenericClass<String> gc = new GenericClass<String>();
    }
}

メソッドで使用するテンプレートは、メソッドの可視性のあとに使用するテンプレートを指定した。クラスの場合は、リスト8のようにクラス名の後に<E>のようにそのクラスで使用する名前を指定する。

リスト8 テンプレートの指定

public class GenericClass<E>

リスト8のように指定されていると、リスト9のようにそのクラスで使われるEはテンプレートとして使うことができるようになる。

リスト9 クラス内で使用する

    private E element;

このようにクラスを作成すると、そのクラスのインスタンスを作成するときにリスト10のようにインスタンスを生成できるようになる。前回と前々回に紹介したGenericに対応したAPIを使う時の新しい記述ができるようになるわけである。

リスト10 インスタンス生成時に使用するクラスを指定できる

        GenericClass<String> gc = new GenericClass<String>();

この記述方法も、クラスでテンプレートを扱う場合の基本的なものとなるから、覚えてしまおう。なお、リスト6とリスト7を逆コンパイルしたものをリスト11とリスト12に示しておく。メソッドの場合と同じく、実際にテンプレート部分はObjectに変更されていることがわかるだろう。

リスト11 GenericClass.javaをコンパイルして逆コンパイルしたもの

public class GenericClass
{
    private Object element;
}

リスト12 Test2.javaコンパイルして逆コンパイルしたもの

public class Test2
{
    public static void main(String args[])
    {
        GenericClass genericclass = new GenericClass();
    }
}

Genericプログラミングの基本はこのふたつだ。そのほかの書き方としては、テンプレートにおける継承の指定や、複数の値をテンプレートする場合の記述方法などがあり、機能としては細かい規則などがある。これらについては次回から重要なポイントを取り上げて紹介していく。


댓글 없음:

댓글 쓰기