Javaの一般的なワイルドカードについていくつか質問があります。
違いは何ですか List<? extends T>
およびList<? super T>
?
境界のあるワイルドカードとは何ですか?また、境界のないワイルドカードとは何ですか?
最初の質問で、<? extends T>
および<? super T>
は、境界付きワイルドカードの例です。無制限のワイルドカードは<?>
、および基本的には<? extends Object>
。大まかに言って、ジェネリックはどのタイプでもかまいません。境界のあるワイルドカード(<? extends T>
または<? super T>
)extend特定のタイプ(<? extends T>
は上限として知られています)、または特定のタイプの祖先である必要があります(<? super T>
は下限として知られています)。
Javaチュートリアルには、記事 Wildcards および More Fun with Wildcards でジェネリックのかなり良い説明があります。
クラス階層Aがある場合、BはAのサブクラスであり、CとDは両方とも以下のようにBのサブクラスです
class A {}
class B extends A {}
class C extends B {}
class D extends B {}
それから
List<? extends A> la;
la = new ArrayList<B>();
la = new ArrayList<C>();
la = new ArrayList<D>();
List<? super B> lb;
lb = new ArrayList<A>(); //fine
lb = new ArrayList<C>(); //will not compile
public void someMethod(List<? extends B> lb) {
B b = lb.get(0); // is fine
lb.add(new C()); //will not compile as we do not know the type of the list, only that it is bounded above by B
}
public void otherMethod(List<? super B> lb) {
B b = lb.get(0); // will not compile as we do not know whether the list is of type B, it may be a List<A> and only contain instances of A
lb.add(new B()); // is fine, as we know that it will be a super type of A
}
境界付きワイルドカードは? extends B
ここで、Bは何らかのタイプです。つまり、タイプは不明ですが、「バインド」を配置できます。この場合、それはBのサブクラスであるクラスによって制限されます。
Josh Blochは、この中でsuper
とextends
をいつ使用すべきかについての良い説明も持っています google ioビデオトーク 彼が言及している場所プロデューサーextends
コンシューマーsuper
ニーモニック。
プレゼンテーションのスライドから:
_
Stack<E>
_にバルクメソッドを追加するとします。
void pushAll(Collection<? extends E> src);
– srcはEプロデューサーです
void popAll(Collection<? super E> dst);
– dstはEコンシューマです
型パラメーターに渡すことができる型の種類を制限したい場合があります。たとえば、数値を操作するメソッドは、Numberまたはそのサブクラスのインスタンスのみを受け入れたい場合があります。これが境界型パラメーターの目的です。
Collection<? extends MyObject>
IS- A MyObject(つまり、myObject型のオブジェクト、またはMyObjectのサブクラスのオブジェクト)またはMyObjectクラスのオブジェクトとの関係を持つすべてのオブジェクトを受け入れることができることを意味します。
例えば:
class MyObject {}
class YourObject extends MyObject{}
class OurObject extends MyObject{}
その後、
Collection<? extends MyObject> myObject;
myObjectまたはMyObjectの子のみを受け入れます(つまり、OurObject型またはYourObject型またはMyObject型のオブジェクトは受け入れますが、MyObjectのスーパークラスのオブジェクトは受け入れません)。
一般に、
構造に
? extends E
という形式のタイプの要素が含まれている場合、構造から要素を取得できますが、構造に要素を配置することはできません
List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14); // compile-time error
assert ints.toString().equals("[1, 2, 3.14]");
要素を構造に入れるには、Wildcards with super
と呼ばれる別の種類のワイルドカードが必要です。
List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
List<Integer> ints = Arrays.asList(5, 6);
Collections.copy(objs, ints);
assert objs.toString().equals("[5, 6, four]");
public static <T> void copy(List<? super T> dst, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) {
dst.set(i, src.get(i));
}
}
public class A { }
public class B extends A { }
public class C extends A { }
List<A> listA = new ArrayList<A>();
List<B> listB = new ArrayList<B>();
問題
listB = listA; //not compiled
listA = listB; //not compiled
listB = listA;
listAでは、AのインスタンスまたはAのサブクラス(BおよびC)であるオブジェクトを挿入できます。 listB = listA
を作成すると、listA
に非Bオブジェクトが含まれる危険があります。その後、getlistB
からオブジェクトを削除しようとすると、B以外のオブジェクト(AやCなど)が削除される危険があります。これは、listB
変数宣言の規約に違反します。
listA = listB;
この割り当てを行うことができれば、挿入listB
が指すリストにAおよびCインスタンスを作成できます。 Listであると宣言されているlistA
参照を介して行うことができます。したがって、挿入 B以外のオブジェクトを、B(またはBサブクラス)インスタンスを保持するように宣言されたリストに入れることができます。
ご覧のとおり、問題はassignmentsにあります。たとえば、この方法で、相対的なリストの要素を処理するメソッドを作成することはできません。
ジェネリックワイルドカードによる救助
List <? extends A>
aka 上限 aka covariance aka producersを使用する必要がありますread .get()リストから
コレクション内のインスタンスがAのインスタンスまたはAのサブクラスであることがわかっている場合、読み取りしても安全コレクションのインスタンスであり、それらをAインスタンスにキャストします。
挿入できません要素をリストに追加します。リストがクラスA、B、またはCに入力されているかどうかわからないためです。
List <? super A>
aka 下限 aka contravariance aka consumersを使用する必要がありますinsert .add()リストへ
リストがAまたはAのスーパークラスに入力されていることがわかっている場合、挿入しても安全 AのインスタンスまたはAのサブクラス(例:BまたはC)がリストに入力されます。
ただし、読み取りオブジェクトをObjectにキャストする場合を除き、リストから読み取り不可になります。リストに既に存在する要素は、AまたはAのスーパークラスのいずれかのタイプである可能性がありますが、どのクラスであるかを正確に知ることはできません。
詳細をお読みください こちら
汎用ワイルドカードは、コレクションで動作するメソッドをより再利用可能にするために作成されます。
たとえば、メソッドにパラメータList<A>
がある場合、このメソッドにはList<A>
のみを指定できます。状況によっては、この方法の機能にとっては無駄です。
List<A>
からのみオブジェクトを読み取る場合、このメソッドにList<A-sub>
を与えることを許可する必要があります。 (A-sub IS a A)List<A>
にのみ挿入する場合、List<A-super>
をこのメソッドに許可する必要があります。 (A IS a-super)