Wiki内検索
カウンタ
メニュー
最近更新したページ
最新コメント
トップページ by awesome things!
トップページ by check it out
Generics(Java) by stunning seo guys
String(Java) by awesome things!
トップページ by check it out
PHPのページ by stunning seo guys
Tomcatのページ by stunning seo guys
トップページ by check it out
トップページ by awesome things!
タグ

Generics(Java)


Generics

Generics(ジェネリクス)は、Java5.0の目玉の一つです。
このGenericsとAutoboxing/Unboxing拡張for文によって、プログミングが随分とすっきりするようになりました。

また、これまでObject型しか格納できないCollectionフレームワークには、間違ったクラスを格納してしまった場合、それが(Collectionからの取り出し時の)ClassCastExceptionという実行時例外(Runtime Exception)でしか捕捉出来ないといった問題がありました。
Genericsの導入によって、Collectionに(格納する)「型」を宣言することで、間違った型を格納することが「コンパイルエラー」として発現させることができるようになりました。これにより、コード品質の向上が見込めます。


簡単なGenericsのサンプルプログラム

ListとMapを使うコーディングかんたんなサンプルを作ってみます。
まず、1.4系で以下のようなコードを書いてみます。

package test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ExampleGenerics1 {

    public static void main(String[] args) {

        // Listに整数1を追加する。
        List aList = new ArrayList();
        aList.add(new Integer(1)); // (A) 1をラッパークラスIntegerでラップする。
        System.out.println("listed value = " + aList.get(0));

        // 上のListから整数1を取り出す。
        // (B) 取り出してラッパークラスにCastした後、1に足す。
        int sum = 1 + ((Integer)aList.get(0)).intValue();
        System.out.println("test sum = " + sum);

        // 整数をキーにするMapを作る。
        Map map = new HashMap();
        map.put(new Integer(1),"いちご"); // (C) キーとなる整数をラップする。
        map.put(new Integer(2),"ばなな");

        // (D) 1番目を取り出すために1をIntegerでラップして、取り出す。
        System.out.println("first element is " + map.get(new Integer(1)));
    }
}

これを実行すると以下のような結果が得られます。

listed value = 1
test sum = 2
first element is いちご

これと同じ結果を得られるコードをGenerics+Autoboxing/Unboxingを使って記述してみます。

package test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SampleGenerics1 {

    public static void main(String[] args) {

        // Listに整数1を追加する。
        List<Integer> aList = new ArrayList<Integer>(); // (a) GenericsでListの型を宣言。
        aList.add(1);                // (b) Autoboxingで整数をそのままadd。
        System.out.println("listed value = " + aList.get(0));

        // 上のListから整数1を取り出す。
        int sum = 1 + aList.get(0); // (c) Auto-unboxingで整数をそのまま取り出す。
        System.out.println("test sum = " + sum);

        // 整数をキーにするMapを作る。
        Map<Integer,String> map = new HashMap<Integer,String>(); // (d) GenericsでMapを宣言。
        map.put(1,"いちご"); // (e) Auto-unboxingで整数をKeyにそのまま取り出す。
        map.put(2,"ばなな");

        System.out.println("first is " + map.get(1));
    }
}

GenericsはCollectionに対して、「それに格納されるオブジェクトの型」を宣言する機構を提供します。
上の(a)では、Listの中身はIntegerだぞと宣言しています。これと同様に、(d)ではMapに対して2つ取る引数を、それぞれIntegerとStringと宣言しています。

Genericsによって、このように「型」がプログラムレベルで規定されてしまうと、(b)、(c)、(e)のようにAutoboxing/Unboxingによって、プリミティブ型⇔参照型の変換が自動的に行えるようになります。
これらの一連の機構が、(実行時ではなく)コンパイルのレベルで機能するところも良い点です。なぜなら、前にも述べましたように、実行時例外を防止できること、Castという(わずらわしい上に)コスト高の処理を行わなくて良いからです。


Genericsと型引数

それではGenericsの意味するところを、もうちょっとよく見てみましょう。
上のサンプルで、Genericsを利用した箇所は以下の2箇所でした。

        List<Integer> aList = new ArrayList<Integer>(); // (a) GenericsでListの型を宣言。
        …
        Map<Integer,String> map = new HashMap<Integer,String>(); // (d) GenericsでMapを宣言。

まず、これを見て分かることは、
  1. Collectionフレームワークのインターフェイス(List、Map)と、実装クラス(ArryaList、HashMap)に<E>(Eは「型」を表す)という引数を受け取るような変更が加えられたこと。(これを「型変数」といいます)
  2. 実装クラス名<E>()がデフォルトのコンストラクタであること。
といったことです。

実際、Java5.0ではCollectionフレームワークのインターフェイスとして、Queueインターフェイス(LinkedListが一つの実装クラスになっています)が追加され、Setインターフェイスとともに「型引数」を受け取るような大幅な修正が加えられています。

また、上の事柄からは以下のことが(ほぼ必然的に)導かれます。
  1. 「型」をインターフェイスやクラスの引数に取っている以上、内部のメソッドにも型を引数ととることを許している。
  2. 引数として渡す「型」は実装クラスではなく、抽象クラス、もしくはインターフェイスを指向すべきこと。それにより、クラス間の祖結合を指向できる。

実際、上の「メソッドにも型を引数にとること」を含めた、上の仕様は全てのユーザープログラムに対しても許されるような言語拡張がなされています。
つまり、
  • すべてのインターフェイス、クラス、メソッドには「型」を引数としてとることが許される。
ということです。

実際には、「型引数」の渡し方として<E>だけではなく、<E extends T>という渡し方も可能となっており、Java5.0での言語仕様の拡張が大変大きなものであったことが分かります。


もっと突っ込んだGenericsの使い方

Genericsと型引数の応用(Java)
2007年02月28日(水) 09:04:01 Modified by wanderingse




スマートフォン版で見る