Genericsとは、C++のテンプレートのような機能で、クラスやインタフェースをパラメータ化するための技術だそうです。Genericsを使うことによって、
などいろいろな効果があるようです。
たとえば
List list = new ArrayList(); list.add(new Integer(10)); String str = (String) list.get(0);
などのエラーは実行時しか検知できませんか、Genericsを使って記述すると
List<Integer> list = new ArrayList<Integer>(); list.add(new Integer(10)); String str = (String) list.get(0); <-コンパイルエラーとなる
となりコンパイルエラーとなります。コンパイル時にエラーとなるので、保守性が向上するわけですね。
さて構文ですが、
List<Integer> list = new ArrayList<Integer>();
などとクラス名<クラス名>という構文になります。http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/List.html を見てもわかるとおりJavaDoc?も
インタフェース List<E>
と宣言されています。
先はListの例でしたが、次はIteratorです。Listの返り値はパラメタ化Iteratorなので e.next() したときにIntegerにキャストがいらなくなります。
public class GenericsSample { public static void main(String[] args) { List<Integer> list = new ArrayList<Integer>();//IntegerでListを作成 for (int i = 0; i < 10; i++) { list.add(new Integer(i)); } Iterator<Integer> e = list.iterator();//このIteratorはIntegerのIterator while (e.hasNext()) { Integer integer = e.next(); //Integerにキャストがいらない System.out.println(integer); } } }
次はMapです。key,valueがあるので
Map<String, Integer> map = new HashMap<String, Integer>();
という指定になります。
public static void main(String[] args) { Map<String, Integer> map = new HashMap<String, Integer>(); for (int i = 0; i < 10; i++) { map.put("key_" + Integer.toString(i), new Integer(i)); } Set<String> keys = map.keySet(); //返り値のSetはStringのSet Iterator<String> e = keys.iterator(); //そのSetのIteratorはStringのIterator while (e.hasNext()) { String key = e.next(); //だからキャストがいらない Integer integer = map.get(key); //ここもキャストがいらない。 System.out.println(key + ":" + integer); } }
Genericsの使い方は何となく分かってきたので、次はGenericsなクラスを自分で作ってみたいと思います。作る観点というかどういうときにGenerics型を導入するかというと、たとえばインタフェースが共通ってわけじゃないけど同じ機能を提供したいとき、といえばいいでしょうか。たとえば java.util.List<E> に定義された
E get(int index);
などは、Eはインタフェースは共通ではないけど(共通があるとしたらjava.lang.Objectですね)、getでindex番目のオブジェクトを返す機能という意味では共通ですね。
他には、あるサービスを取得するメソッドを作ろうと思ったときに(各サービスは、メソッド名が共通じゃないから同じインタフェースにはできない)
<T> T getService(Class type);
みたいにクラスを渡してサービスを取得するメソッドをパラメタ化する、とかですかね。ちなみにこれはメソッドのパラメータ化といって、クラス全体じゃなくてメソッドだけをパラメタ化しています。(後述)
Genericsなクラスを作るには
public interface GenericDao<T, PK extends Serializable> { PK create(T newInstance); T read(PK id); void update(T transientObject); void delete(T persistentObject); }
などとクラスの定義でGenericDao?<T> などと書けばOKです。
上の例の
PK extends Serializable
の部分は引数はSerializableなら何でもいいって事になります。
public class Hoge<T> { private T t; public Hoge(T t) { this.t = t; } public void exe(List<T> list) {} }
のばあい、コンストラクタの引数がTなので、TをListとすると
List list=new ArrayList(); Hoge<List> hoge=new Hoge<List>(list);
ってなります。listはListのサブクラスでもOKです。しかし
public void exe(List<T> list) {}
に関してはたとえばTをNumberだとすると
List<Integer> list = new ArrayList<Integer>(); hoge.exe(list);
はコンパイルエラーとなります。listはList<Integer>なのでList<Number>とは互換性がないって事ですね。
Tのサブクラス、この例だとNumberのサブクラスならいいよって定義したいときは
前: public void exe(List<T> list) {} 後: public void exe(List<? extends T> list) {}
とします。?のワイルドカードを使って、Listの型はTのサブクラスなら何でもいいよっていう定義となります。その他、TのSuperクラスならいいよとか、Tは何でもいいよって書きたいときはそれぞれ
public void exe(List<? super T> list) {} public void exe(List<?> list) {}
ってかきます。
なんだかわけがわからなくなってきました。
クラスのパラメータ化ではなくて、メソッドだけのパラメータ化って事もできます。たとえば
public class ServiceFactory { private Map map = new HashMap(); public <T> T createService(Class<T> type) { return (T) map.get(type); } }
と戻り値の前にGenericsの型を書いておくことで
ServiceFactory factory = new ServiceFactory(); List list = factory.createService(List.class);
そのメソッドだけをパラメータ化することができます。つまりList.classってのを渡したらList型が返りますってことができるわけです。フレームワークとかを作るときに使いそうですね。型を指定して、この型のサービスくれーみたいな。。
この記事は
現在のアクセス:15994