次のtest()
メソッドに対して、単純なMap<?, ? extends List<?>>
ではなくジェネリック型Map<?, List<?>>
を使用する必要があるのはなぜですか?
public static void main(String[] args) {
Map<Integer, List<String>> mappy =
new HashMap<Integer, List<String>>();
test(mappy);
}
public static void test(Map<?, ? extends List<?>> m) {}
// Doesn't compile
// public static void test(Map<?, List<?>> m) {}
次のように機能し、3つのメソッドはいずれにしても同じ消去タイプを持っていることに注意してください。
public static <E> void test(Map<?, List<E>> m) {}
基本的に、_List<List<?>>
_と_List<? extends List<?>>
_には異なる型の引数があります。
実際には、一方が他方のサブタイプである場合がそうですが、最初に、それらが個々に何を意味するかについてさらに学習しましょう。
一般的に言って、ワイルドカード_?
_は、「欠落している情報」を表します。これは"ここでは型引数が1回ありましたが、それが何であるかはもうわかりません"です。また、それが何であるかわからないため、その特定の型引数を参照するものをどのように使用できるかに制限が課されます。
とりあえず、List
の代わりにMap
を使用して例を簡略化しましょう。
_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
に入れることができますが、そうすることでtheir elementsの知識が失われます。
_? 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<?>>
_を作成できます。そこにどのタイプのList
が含まれているかはわかりません。また、要素のタイプthoseList
sもわかりません。つまり:
_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
sの要素タイプ。
(ただし、割り当てあらゆる種類のリストを保持するリストにできることに注意してください...)
それを分解するには:
_// ┌ 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
_が必要な理由"concrete" ジェネリック型にはinvarianceがあること、つまり _List<Dog>
_は_List<Animal>
_ のサブタイプではないことがわかります= _class Dog extends Animal
_の場合でも。代わりに、ワイルドカードはcovariance、つまり_List<Dog>
_ isのサブタイプであり、_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
に適用します。
List<String>
_は_List<?>
_のサブタイプですが、_List<List<String>>
_はnot _List<List<?>>
_のサブタイプです。前に示したように、これはList
に間違った要素を追加することによって型の安全性を損なうことを防ぎます。List<List<String>>
_ is _List<? extends List<?>>
_のサブタイプ。制限付きワイルドカードは共分散を許可するためです。つまり、_? extends
_は、_List<String>
_が_List<?>
_のサブタイプであることを考慮することを許可します。_List<? extends List<?>>
_は、実際には共有スーパータイプです。
_ List<? extends List<?>>
╱ ╲
List<List<?>> List<List<String>>
_
Map<Integer, List<String>>
_は、値としてonly _List<String>
_を受け入れます。Map<?, List<?>>
_は、値としてanyList
を受け入れます。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
の-allMap
sが同じ要素タイプを持っている必要があります。そのタイプが実際には何であるかはわかりませんisですが、抽象的な方法で使用できます。これにより、「ブラインド」操作を実行できます。
たとえば、この種の宣言は、ある種の蓄積に役立つ場合があります。
_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;
}
_
key typeが何であるかわからないため、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
とその中のすべてを操作する方法について許容します。
? extends
_と_? super
_の違いについて。