例1
import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { List2<Object> list = new List2<>(); List<String> sList = new ArrayList<>(); List<Object> oList = new ArrayList<>(); //List<String>はList<Object>のサブクラスではないので以下の呼び出しはコンパイルエラー list.merge(oList, sList); } } class List2<E> extends ArrayList<E> { public void merge(List<E> l1, List<E> l2){ this.addAll(l1); this.addAll(l2); } }
import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { List2<Object> list = new List2<>(); List<String> sList = new ArrayList<>(); List<Object> oList = new ArrayList<>(); //List<? extends E>の型パラメータはEのサブクラスであるため以下は許容される list.merge(oList, sList); } } class List2<E> extends ArrayList<E> { public void merge(List<E> l1, List<? extends E> l2){ this.addAll(l1); this.addAll(l2); } }
例2
import java.util.ArrayList; import java.util.Collection; public class Test { public static void main(String[] args) { List2<String> list = new List2<>(); //ArrayList<Object>はCollection<String>の親クラスではない list.merge(new ArrayList<Object>()); } } class List2<E> extends ArrayList<E> { /** * 引数のコレクションに要素を全て追加 */ public void merge(Collection<E> collection){ collection.addAll(this); } }
import java.util.ArrayList; import java.util.Collection; public class Test { public static void main(String[] args) { List2<String> list = new List2<>(); //ObjectはStringの親クラスであるため以下は許容される list.merge(new ArrayList<Object>()); } } class List2<E> extends ArrayList<E> { /** * 引数のコレクションに要素を全て追加 */ public void merge(Collection<? super E> collection){ collection.addAll(this); } }
PECS
extendsの基準
メソッド内で入力された型パラメータの変数を使用する場合はextends
なんで?
メソッド内で入力された引数が、型パラメータに指定された型のサブクラスであった場合、親クラスとして使用しても問題ないから
superの基準
呼び出し元に返す(もしくは引数として破壊される対象)のオブジェクトにはsuper
なんで?
呼び出し元で型パラメータ化した変数にとって、その型パラメータのサブクラスを型パラメータ化した型として扱っても問題ないから
大事な前提条件
全ての子クラスは親クラスとしても使えなければならない(リスコフの置換原則)
import java.util.ArrayList; import java.util.Collection; public class Test { public static void main(String[] args) { ExtendsList<Child> list = new ExtendsList<>(); //これが許容されないのは何故だろう Collection<Object> objs = list.merge(new ArrayList<Child>()); } } class ExtendsList<E> extends ArrayList<E> { // 戻り値はEの親のコレクションとして使いたい 引数はEの子のコレクションとして使いたい public Collection<? super E> merge(Collection<? extends E> collection){ this.addAll(collection); return this; } } class Child extends Object{ } class ChildChild extends Child{ }
これが通らないのは何故だろう
Collection
import java.util.ArrayList; import java.util.Collection; import java.util.List; public class Test { public static void main(String[] args) { ExtendsList<Child, Object> list = new ExtendsList<>(); Collection<Object> objs = list.merge(new ArrayList<Child>()); } } class ExtendsList<E extends T, T> extends ArrayList<E> { public Collection<T> merge(Collection<E> collection){ List<T> list = new ArrayList<>(); list.addAll(collection); return list; } } class Child extends Object{}
こんな感じにしたい場合は? ExtendedListの型パラメータにObjectを指定しないでこれをできてもいいような
こんなん?
import java.util.ArrayList; import java.util.Collection; import java.util.List; public class Test { public static void main(String[] args) { ExtendsList<Object> list = new ExtendsList<>(); Collection<Object> objs = list.merge(new ArrayList<Child>()); } } class ExtendsList<T> extends ArrayList<T> { public Collection<T> merge(Collection<? extends T> collection){ List<T> list = new ArrayList<>(); list.addAll(collection); return list; } } class Child extends Object{}
ExtendsListのインスタンスを最初からChildで宣言するのは無理なのかな
import java.util.ArrayList; import java.util.Collection; import java.util.List; public class Test { public static void main(String[] args) { ExtendsList<Child> list = new ExtendsList<>(); Collection<Parent> objs = list.<Parent>merge(new ArrayList<Child>()); } } class ExtendsList<T> extends ArrayList<T> { public <E super T> Collection<E> merge(Collection<? extends E> collection){ List<E> list = new ArrayList<>(); list.addAll(collection); return list; } } class Parent{} class Child extends Parent{}
なんとなくこんなんでいける気がするけどコンパイルが通らない