genericsのboundというか、?の動作

genericsのboundってよーわからんとのことなので、調子に乗って説明しちゃうぞ!

List<?>の動作

↓のコードがコンパイルエラーになるのはなんで?なんでなの?

private <T> void add(List<? extends T> list, T t){
    list.add(t);
}

// ==> error
// 型 List<capture#1-of ? extends T> のメソッド add(capture#1-of ? extends T) は 引数 (T) に適用できません。

genericsのboundってよーわからん - ハタさんのブログ

一言で説明するとList<?>にはaddできません。これはList<?>とかList<? extends Hoge>は不定インスタンス*1を持つわけで、addできるかわからないからだと思われます。


例えば、以下のコードで考えます。

public static void main(String[] args) {
    List<? extends java.util.Date> dateList = null;
    List<java.util.Date> utilDateList = new ArrayList<java.util.Date>();
    List<java.sql.Date> sqlDateList = new ArrayList<java.sql.Date>();
    
    dateList = utilDateList;  //(1)
    dateList = sqlDateList;   //(2)
}

これはエラーなく通ります。
このとき、(1)の状態でのdateListのインスタンスArrayList<java.util.Date>なので、そのインスタンスにはjava.util.Dateをaddできる。が、(2)の状態でのdateListのインスタンスArrayList<java.sql.Date>なので、java.util.Dateはaddできない。つまり、List<? extends java.util.Date> dateListというモノに対して、java.util.Dateはaddできるかどうかわからない。だからjava的にはerrorとしてるわけなんでしょう。

↓のコードが通るのはなんで?なんでなの?

private <T, W extends T> void add(List<W> list, W w){
    list.add(w);
}

genericsのboundってよーわからん - ハタさんのブログ

ということで、TとかWには関係ないので、このコードは通るわけです。


…なんて長々と説明しちゃいましたが、この辺りは[Java][Generics] SetとSetの違いまとめ - うなの日記とか[Java][Generics] SetとSetの違いまとめ - うなの日記がよくまとまっていて参考になると思います。

使いどころ

で、気になるList<?>の使いどころですが、例えば抽象メソッドの実装なんかで微妙に使えたりします。

import java.util.ArrayList;
import java.util.List;

public abstract class AbstractClass {
    public abstract List<? extends java.util.Date> newList();
    //List<java.util.Date>だと、SubClass2#newListでerror
    //Listで型指定なしだと、通ることは通るんだけど。
    
    //もちろん、getしたらjava.util.Dateクラスで受け取れる。
}

class SubClass1 extends AbstractClass{
    @Override
    public List<java.util.Date> newList() {
        return new ArrayList<java.util.Date>();
    }
}

class SubClass2 extends AbstractClass{
    @Override
    public List<java.sql.Date> newList() {
        return new ArrayList<java.sql.Date>();
    }
}

他にもあれば教えてください><

ちなみに、

List<?> list = new ArrayList<Object>()

もエラーになることから、宣言したときの方は一緒じゃないといけないんだろーなーっていう。
genericsのboundってよーわからん - ハタさんのブログ

うしろー!うしろー!

*1:と呼んでいいのかわかりませんが。