日々常々

ふつうのプログラマがあたりまえにしたいこと。

ジェネリック型

J2SE 5.0で追加されてもう6年以上が経過しているジェネリック型ですが、未だに使い方がよく判らなかったり、手間なだけと思っている方や、酷いものでは「@SuppressWarnings("unchecked")をつけるもの」って認識されていたりします。ざっくり説明するのにもっとも楽なのがCollectionなので、今回はCollectionを例に挙げて説明します。

単純なものの書き方は悩む事も無いでしょう。

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

確かに記述の手間が増える場合もあります。上記の例ではが2回出ている点は冗長に感じます。まとめて書ければ良いのにとは常々思うのですが、フィールドの型「List」とインスタンス生成「new ArrayList()」はそれぞれ独立していて、代入演算子「=」によって繋げられているに過ぎません。当然、インスタンス生成を行わずメソッドの戻り値を使用する事も出来るので、省略は出来なさそうです。

ジェネリック型を理解できていない方はこんな風に書いたりします。コンパイラの警告を無くす為だけの、意味の無い記述。多くの場合、この記述は誤りです。勿論正しい場合もありますけれど。

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

JavaのCollectionはあらゆるオブジェクトを格納する事が出来ますが、大抵の場合、格納されるオブジェクトには共通性が存在します。最初の例では、Stringクラスです。Collectionを扱う場合、オブジェクトの格納時や、使用時には、型が確定します。
J2SE 1.4以前では、Collectionへの格納時には特に意識せず行い、取得後にキャストする事で求める型で扱っていました。

List list = new ArrayList();
list.add("abc");

String str = (String)list.get(0);

ジェネリック型を使用すると以下のようになります。

List<String> list = new ArrayList<String>();
list.add("abc");

String str = list.get(0);

ジェネリック型を使用することで、キャストの記述が不要になります。(内部的にキャストはしています。)Listに格納されているオブジェクトは全てStringであることが判り切っているためです。ジェネリック型を使用しない場合、使用箇所が増えれば増えるほど、キャストの記述がどんどん冗長になります。また、格納箇所と使用箇所が離れれば、格納した型が何かを確認する必要が出てきます。複数箇所に同じ事を書く必要があるため、キャストが定型句やおまじないレベルになってしまいます。使用する事で、コードの可読性が向上します。

ジェネリック型は、オブジェクトに依存しないものを扱う際に特定の振る舞いをさせたい場合に使用します。振る舞いを指定るためには、ジェネリック型は無くても、適切なインタフェースやクラスを常に定義することで同様の事は可能になります。先の例で言うと、Listが使われる所にStringListとかいうインタフェースとその具象クラスを作ってしまえば出来る訳です。でも、扱うオブジェクトが違うだけで、この場合コレクションインタフェース、クラスの振る舞いは変わりません。これはDRYに反するわけで、コードが散らかるのでNGです。
Listとされても、Listの振る舞いは変わりません。Listを扱う側のクラスが変わります。となると、Listは格納する型がStringであるという情報をインタフェースとしては持つべきではありません。それはListの責任ではないので、StringListを作るってのはNGでしょう。


長々と書きましたが、使う側に説明するときは「キャストの手間を減らせます」でいいと思います。作る側は最低限「プログラミング言語Java第4版」の「第11章 ジェネリック型」程度の知識は持って欲しいと思います。使う側にも持っていて欲しいのですが、少なくとも私の周囲では高望みです。

プログラミング言語Java (The Java Series)

プログラミング言語Java (The Java Series)

  • 作者: ケン・アーノルド,ジェームズゴスリン,デビッドホームズ,柴田芳樹
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2007/04
  • メディア: 単行本
  • 購入: 38人 クリック: 1,044回
  • この商品を含むブログ (73件) を見る