共変戻り値型に使えるワイルドカード
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
そこで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追加)
先行記事があった。
トップ