共変戻り値型に使えるワイルドカード

clone()の戻り値型は、共変戻り値型 (covariant return type) が使えることを利用して、定義されたクラスの型にするのがよいとされている。

参考:
http://www.ne.jp/asahi/hishidama/home/tech/java/clone.html#h2_covariant

では、コピーをリストで複数返したい場合はどうするか?(クラスはAmeba, メソッド名はspawn() とする)

package test;

import java.util.Arrays;
import java.util.List;


public class CovariantTest {
    public static class Ameba implements Cloneable {

        // 共変戻り値型
        @Override
        protected Ameba clone() {
            try {
                return (Ameba) super.clone();
            } catch (CloneNotSupportedException e) {
                throw new AssertionError(
                        "unexpected CloneNotSupportedException", e);
            }
        }
        
        public List<Ameba> spawn() {
            // clone を複数返す
            return Arrays.asList(clone(), clone());
        }
    }
    
    public static class StrongAmeba extends Ameba {

        // 共変戻り値型
        @Override
        protected StrongAmeba clone() {
            return (StrongAmeba) super.clone();
        }

        // コンパイルエラー。List<StrongAmeba> だとオーバーライドできない
//        @Override
//        public List<StrongAmeba> spawn() {
//            return Arrays.asList(clone(), clone(), clone());
//        }
        
    }
}

Listの型パラメータをサブクラスStrongAmebaにしたいが、ListはListのサブタイプでないのでオーバーライドできない。
そこでspawnの戻り値型の型パラメータを上限境界つきワイルドカード List<? extends Ameba> にする。

package test;

import java.util.Arrays;
import java.util.List;


public class CovariantTest2 {
    public static void main(final String[] args) {
        List<? extends Ameba> amebas = new Ameba().spawn();
        List<? extends StrongAmeba> strongAmebas = new StrongAmeba().spawn();
        List<? extends VeryStrongAmeba> veryStrongAmebas = new VeryStrongAmeba().spawn();
        
        System.out.println(amebas);
        System.out.println(strongAmebas);
        System.out.println(veryStrongAmebas);
    }
    
    public static class Ameba implements Cloneable {

        // 共変戻り値型
        @Override
        protected Ameba clone() {
            try {
                return (Ameba) super.clone();
            } catch (CloneNotSupportedException e) {
                throw new AssertionError(
                        "unexpected CloneNotSupportedException", e);
            }
        }
        
        // 上限境界付きワイルドカード
        public List<? extends Ameba> spawn() {
            // clone を複数返す
            return Arrays.asList(clone(), clone());
        }
    }
    
    public static class StrongAmeba extends Ameba {

        // 共変戻り値型
        @Override
        protected StrongAmeba clone() {
            return (StrongAmeba) super.clone();
        }

        // 共変戻り値型
        @Override
        public List<? extends StrongAmeba> spawn() {
            return Arrays.asList(clone(), clone(), clone());
        }
        
    }
    
    public static class VeryStrongAmeba extends StrongAmeba {

        // 共変戻り値型
        @Override
        protected VeryStrongAmeba clone() {
            return (VeryStrongAmeba) super.clone();
        }

        // 共変戻り値型
        @Override
        public List<? extends VeryStrongAmeba> spawn() {
            return Arrays.asList(clone(), clone(), clone(), clone());
        }
        
    }
}

実行結果

[test.CovariantTest2$Ameba@ee22f7, test.CovariantTest2$Ameba@39ab89]
[test.CovariantTest2$StrongAmeba@2cb49d, test.CovariantTest2$StrongAmeba@105d88a, test.CovariantTest2$StrongAmeba@cb6009]
[test.CovariantTest2$VeryStrongAmeba@e28b9, test.CovariantTest2$VeryStrongAmeba@193a66f, test.CovariantTest2$VeryStrongAmeba@93d6bc, test.CovariantTest2$VeryStrongAmeba@1f6df4c]

しかしpublicメソッドの戻り値にワイルドカードってあんまり見ない。どうなんだろう。

まとめ

リストを返すメソッドをサブクラスでrefineしたい場合は、もとの定義を上限境界つきワイルドカードに

関連リンク

ワイルドカードのサブタイプ関係については以下がわかりやすい。
Wildcards and Subtyping (The Java™ Tutorials > Learning the Java Language > Generics (Updated))

上限境界つきワイルドカードを使うとadd(E) などの型パラメータを引数に含むメソッドが呼び出せなくなる。以下がわかりやすい
Java総称型のワイルドカードを上手に使いこなすための勘所 - 達人プログラマーを目指して

(9/10追加)
先行記事があった。
トップ