Mapの値にListを持つ
要求:
Mapの値がListで,かつこのListで管理するオブジェクトにChild extends Parent
という関係がある.そして,各Listのインスタンスが存在し,Listの中のオブジェクトをParentとして処理したい.
問題点
ジェネリクスは共変じゃないので,以下ができない
Map<Key, List<Parent>> map1;
Map<Key, List<Child>> map2;
map1 = map2; // ここでコンパイルエラー
そこで,ワイルドカードを利用する.しかし,以下もNG
Map<Key, List<? extends Parent>> map1;
Map<Key, List<Child>> map2;
map1 = map2; // ここでコンパイルエラー
これは,
List<? extends Parent> list1;
List<Child> list2;
list1 = list2;
ができるのに,おかしいと思ってしまった.そして,以下が正解
Map<Key, ? extends List<? extends Parent>> map1;
Map<Key, List<Child>> map2;
map1 = map2;
見て分かる通り,ワイルドカードを2回使わないといけなかった.
これを利用してメソッドを定義して実行してみた.
List<Integer> ilist = new ArrayList<Integer>();
ilist.add(1);
ilist.add(2);
ilist.add(3);
Map<String, List<Integer>> imap = new HashMap<String, List<Integer>>();
imap.put("1", ilist);
doit(imap);
public void doit(Map<String, ? extends List<? extends Number>> inmap) {
for (String key : inmap.keySet()) {
System.out.println("key = " + key);
List<? extends Number> list = inmap.get(key);
for (Number n : list) {
System.out.println(n);
}
}
}
おまけ:
List<Child>
からList<Parent>
を値に持つMapを返すメソッド
その1
public <V, K extends T, T> Map<V, List<? extends T>> convert(Map<V, List<K>> srcMap) {
Map<V, List<? extends T>> dstMap = new HashMap<V, List<? extends T>>();
for (V key : srcMap.keySet()) {
dstMap.put(key, srcMap.get(key));
}
return dstMap;
}
その2
public <V, K extends T, T> Map<V, List<T>> toUpper(Map<V, List<K>> srcMap) {
Map<V, List<T>> dstMap = new HashMap<V, List<T>>();
for (V key : srcMap.keySet()) {
List<T> tlist = new ArrayList<T>();
for (K val : srcMap.get(key)) {
tlist.add(val);
}
dstMap.put(key, tlist);
}
return dstMap;
}
おそらく「その2」のほうがよい.何故かと言うと,返り値の型がMap<V, List<T>>
となるから.Effective Javaによると,「その1」のようにこのメソッドを実行側でワイルドカードを意識させる(e.g., List<? extends T>
)ことは良くないとされるため(多分..)
間違い等あればご指摘下さい.