基本的に、List<List<?>>
異なるList<? extends List<?>>
型の引数があります。
実際には一方が他方のサブタイプである場合がありますが、最初にそれぞれが何を意味するのかを詳しく学びましょう。
セマンティックの違いを理解する
一般的に言えば、ワイルドカード?
は「不足している情報」を表します。「ここにはかつて型引数がありましたが、それが何であるかはわかりません」という意味です。そして、それが何であるかわからないため、その特定の型引数を参照するものをどのように使用できるかについて制限が課せられます。
List
とりあえず、 の代わりに を使用して例を単純化しましょうMap
。
Aは、任意の型引数を持つ任意の種類の List をList<List<?>>
保持します。つまり:
List<List<?>> theAnyList = new ArrayList<List<?>>();
// we can do this
theAnyList.add( new ArrayList<String>() );
theAnyList.add( new LinkedList<Integer>() );
List<?> typeInfoLost = theAnyList.get(0);
// but we are prevented from doing this
typeInfoLost.add( new Integer(1) );
任意List
のを入れることができますtheAnyList
が、そうすることで、それらの要素の知識を失ってしまいます。
を使用する? extends
と、 List の特定のサブタイプがList
保持されますが、それが何であるかはわかりません。つまり:
List<? extends List<Float>> theNotSureList =
new ArrayList<ArrayList<Float>>();
// we can still use its elements
// because we know they store Float
List<Float> aFloatList = theNotSureList.get(0);
aFloatList.add( new Float(1.0f) );
// but we are prevented from doing this
theNotSureList.add( new LinkedList<Float>() );
theNotSureList
要素の実際の型がわからないため、に何かを追加することはもはや安全ではありません。(元々は?List<LinkedList<Float>>
それともList<Vector<Float>>
? わかりません。)
これらをまとめてList<? extends List<?>>
. その中にどのタイプが含まれているかはわかりません。また、それらの s のList
要素タイプもわかりません。つまり: List
List<? extends List<?>> theReallyNotSureList;
// these are fine
theReallyNotSureList = theAnyList;
theReallyNotSureList = theNotSureList;
// but we are prevented from doing this
theReallyNotSureList.add( new Vector<Float>() );
// as well as this
theReallyNotSureList.get(0).add( "a String" );
に関する情報と、その中の の要素タイプの両方が失われました。theReallyNotSureList
List
(ただし、リストを保持する任意の種類のリストをそれに割り当てることができることに注意してください...)
それを分解するには:
// ┌ applies to the "outer" List
// ▼
List<? extends List<?>>
// ▲
// └ applies to the "inner" List
はMap
同じように機能しますが、型パラメーターが増えるだけです。
// ┌ Map K argument
// │ ┌ Map V argument
// ▼ ▼
Map<?, ? extends List<?>>
// ▲
// └ List E argument
なぜ? extends
必要なのか
「具体的な」ジェネリック型には不変性があること、つまりifList<Dog>
のサブタイプではないList<Animal>
ことを知っているかもしれませんclass Dog extends Animal
。代わりに、ワイルドカードは共分散をどのように持つか、つまりList<Dog>
のサブタイプですList<? extends Animal>
。
// Dog is a subtype of Animal
class Animal {}
class Dog extends Animal {}
// List<Dog> is a subtype of List<? extends Animal>
List<? extends Animal> a = new ArrayList<Dog>();
// all parameterized Lists are subtypes of List<?>
List<?> b = a;
したがって、これらのアイデアをネストされた に適用しますList
。
審査中
Map<Integer, List<String>>
値としてのみ 受け入れList<String>
ます。
Map<?, List<?>>
any を値として受け入れList
ます。
Map<Integer, List<String>>
およびMap<?, List<?>>
は、別個のセマンティクスを持つ別個の型です。
- 安全でない方法で変更を行うことを防ぐために、一方を他方に変換することはできません。
Map<?, ? extends List<?>>
安全な制限を課す共有スーパータイプです:
Map<?, ? extends List<?>>
╱ ╲
Map<?, List<?>> Map<Integer, List<String>>
ジェネリック メソッドのしくみ
List
メソッドで型パラメーターを使用することにより、具体的な型があるとアサートできます。
static <E> void test(Map<?, List<E>> m) {}
この特定の宣言では、内のすべて List
の がMap
同じ要素型を持つ必要があります。その型が実際に何であるかはわかりませんが、抽象的に使用できます。これにより、「ブラインド」操作を実行できます。
たとえば、この種の宣言は、ある種の累積に役立つ場合があります。
static <E> List<E> test(Map<?, List<E>> m) {
List<E> result = new ArrayList<E>();
for(List<E> value : m.values()) {
result.addAll(value);
}
return result;
}
キーの種類がわからなくなっput
たため、呼び出すことができません。ただし、それらはすべて同じ要素タイプであることがわかっているため、その値を操作できます。m
List
キックのためだけに
質問で説明されていない別のオプションは、境界付きワイルドカードとジェネリック型の両方を使用することList
です。
static <E> void test(Map<?, ? extends List<E>> m) {}
のようなもので呼び出すことができますMap<Integer, ArrayList<String>>
。の型のみを気にする場合、これは最も寛大な宣言ですE
。
境界を使用して型パラメーターをネストすることもできます。
static <K, E, L extends List<E>> void(Map<K, L> m) {
for(K key : m.keySet()) {
L list = m.get(key);
for(E element : list) {
// ...
}
}
}
これは、渡すことができるものについて寛容であると同時に、操作方法m
とその中のすべてについても寛容です。
こちらもご覧ください