【Generics】型パラメータの拡張(1)−extends
型パラメータ(<E>とか)には、<? extends E>という書き方もあります。
今回は、この extends について説明します。
例:Stack クラス
ひとまず、下記のような API の Stack クラスを想定して下さい。
(各メソッドの実装は、説明に影響がないので省略しています。)
public class Stack{ // stack に要素を追加する。 public void push(E e); { } // stack から要素を取り出す。 public E pop() { } // stack が空の場合、true を返す。 public boolean isEmpty() { } }
ここに、複数の要素を一度に push できる pushAll() というメソッドを定義するとします。
こんな感じ。
// 指定された要素を全て stack に追加する。
public void pushAll(Iterable src) {
for (E e : src) {
this.push(e);
}
}
※Iterable は、List とかの親インターフェースで、Iterator を返す iterator() を定義しています。
要は、List とかは Iterable ということです。
問題点
以下のプログラムを実行すると、pushAll() でコンパイルエラーになります。
Stacktarget = new Stack (); List list = new ArrayList (); list.add(12); list.add(34); list.add(56); target.pushAll((Iterable ) list);
これは、Stack と pushAll() の Iterable の型パラメータがぴったり一致していないためです。
(Number と Integer なので、ぴったり一致ではない。)
でも、Number は Integer の親クラス。
使えてもいいのでは?と思うのが人情。
何かいい方法はないか???
解決策
ここで extends を使うとうまく行きます。
public void pushAll(Iterable<? extends E> src) {
追記
混乱するかもしれませんが、下記のコードは正常に動作します。
Stacktarget = new Stack (); target.push(Integer.valueOf(123));
【理由】
- Stack
と push(E e) と、定義上型パラメータが一致している。(pushAll() の場合は Iterable ) - Number は Integer の親クラス。
定義上の型パラメータが一致する場合としない場合とで、対応方法が違うということです。
指針
今回の pushAll のように、型パラメータがクラス定義と一致しない場合かつ、引数の値をプログラム内部で取り出す処理を行う場合、extends と定義するとうまく行きます。
課題
型パラメータには、<? super E> という書き方もあります。
それについては、後日説明します。