2012年12月22日土曜日

ジェネリクスでAPIを柔軟にする

あるクラスの任意のサブクラスのリストを引数に取れるメソッドは、ジェネリクスの型引数にバインドされワイルドカードを指定すれば書ける。

当てずっぽうで書いたらダメだったから何とかその場しのぎのコードを書いていたのだけれど、その後読んだ"Effective Java 2nd Edition"の"Chapter 5 Generics"でいくぶんスッキリしたので整理してみる。

まずリストじゃない場合で前提の確認。Superクラスを継承するSubクラスがあるとき、どちらも引数に取れるメソッドはこう書ける。こう定義しておけば、どちらを渡しても大丈夫。
public void printInstance(Super instance) {
    System.out.println(instance);
}
printInstance(new Super());
printInstance(new Sub());

リストでも同じようにいけるかな? と思って、当てずっぽうで書いたダメな例がこれ。こう書いて、Subのリストを渡そうとすると、コンパイルエラーになる。実は、そもそも、List<Sub>List<Super>のサブクラスではない。
public void printList(List<Super> list) {
    System.out.println(lst);
}
printList(new ArrayList<Sub>);

この場合はBounded wildcard type (バインドされたワイルドカードタイプ)を使って、こう書く。Superのサブクラスのリストだけじゃなくて、Superのリストも渡せる。
public void printList(List<? extends Super> list) {
    System.out.println(lst);
}

反対にあるクラスのスーパークラスのリストとそのクラス自身のリストを引数に取れるようにも書ける。その場合は、<? extends Super>の代わりに<? super Sub>を使う。

使い分けの指針には"PECS"や"get-put principle"がある。よくかみ砕けていないのだけれど、同じことを言っているように思う。こうしておくと型安全で使いやすいAPIになるとのこと。
PECS stands for producer-extends, consumer-super
"Effective Java 2nd Edition", Item 28: Use bounded wilidcards to increase flexibility
構造から値を取得する (get) だけの場合には extends ワイルドカードを使い、構造の中に値を格納する (put) だけの場合には super ワイルドカードを使い、そして両方を行う場合にはワイルドカードを使ってはなりません。
『Java の理論と実践: Generics のワイルドカードを使いこなす、第 2 回』

References

0 件のコメント:

コメントを投稿