Effective Javaを読んでいる㉘

例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はCollection<? super E>の親ではないから?

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{}

なんとなくこんなんでいける気がするけどコンパイルが通らない