知っていると便利な章

4.14. ジェネリック型を引数として渡す場合

『目的』

4.13. ジェネリックメソッドの例ではListのメンバーがすべて同じT型の要素を扱いました。 これは型安全性に配慮し、そのように作ったのだから当然ですね。
しかし、このままではList型にListやListを連結したいような場合には困ってしまいます。
NumberとInteger,Longに継承関係があってもListとListやListには継承関係はないためです。
境界ワイルドカード型を使用して、ポリモーフィズムにも対応できるようにしましょう。

ジェネリックメソッドを使用したリスト連結
以下の例ではconcatGenericをジェネリックメソッドとして定義しています。
この場合、引数としてList,List,Listを混在させることはできません。

// リストの和をとる。
List<Integer> l1 = new LinkedList<>();
l1.add(123);
l1.add(456);
List<Integer> l2 = new LinkedList<>();
l2.add(789);
List<Integer> catList = new LinkedList<>();
concatGeneric(catList, l1, l2);
for (Integer e : catList) {
    System.out.println(e);
}  // 123 456 789

// list1とlist2を連結させる。
private static <T> void concatGeneric(List<T> newList, List<T> list1, List<T> list2) {
    newList.addAll(list1);
    newList.addAll(list2);
}

ここでNumber型にはInteger型の値を格納できるため、以下のようなことをしたくなるかもしれません。 しかし、これはコンパイルエラーになってしまいます。

List<Number> catList = new LinkedList<>();
// concatGeneric(catList, l1, l2); コンパイルエラー

このような場合にはどうしたらいいでしょうか。
境界ワイルドカード型を使用したリスト連結
以下の例では境界ワイルドカード型を使用したジェネリックメソッドとして定義しています。
この場合、引数としてnewListにはTの基底型のリスト、list1,list2にはTの派生型のリストを渡すことができます。
ただし、サンプルコードのコメントにもあるように下限境界ワイルドカード型(super)を使うのはパラメータが代入専用である場合、 上限境界ワイルドカード型(extends)を使うのはパラメータが参照専用である場合にしましょう。
そうしないとポリモーフィズムが崩れてしまうことを確認してください。

// リストの和をとる。
List<Integer> l1 = new LinkedList<>();
l1.add(123);
l1.add(456);
List<Long> l2 = new LinkedList<>();
l2.add(789L);
List<Number> catList = new LinkedList<>();
concatWildCard(catList, l1, l2);
for (Number e : catList) {
    System.out.println(e);
}

// 境界ワイルドカード型
// パラメータが代入専用である場合は下限境界ワイルドカード型を使用する。例)newlist
// パラメータが参照専用である場合は上限境界ワイルドカード型を使用する。例)list1, list2
private static <T> void concatWildCard(List<? super T> newList, 
    List<? extends T> list1, 
    List<? extends T> list2) {
    newList.addAll(list1);
    newList.addAll(list2);
}

< 前のページへ

Pagetop