对于以下Map<?, ? extends List<?>>
方法,为什么必须使用泛型类型Map<?, List<?>>
而不是更简单的test()
?
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) {}
注意以下方法有效,并且三种方法无论如何都具有相同的擦除类型。
public static <E> void test(Map<?, List<E>> m) {}
答案 0 :(得分:49)
从根本上说,List<List<?>>
和List<? extends List<?>>
具有不同的类型参数。
事实上,一个是另一个的子类型,但首先让我们更多地了解它们的含义。
一般来说,通配符?
代表了一些&#34;缺少的信息&#34;。这意味着&#34;这里有一个类型参数,但我们不知道它是什么&#34; 。而且因为我们不知道它是什么,所以对我们如何使用引用该特定类型参数的任何东西施加限制。
目前,让我们使用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
,但这样做会导致我们失去其元素的知识。
当我们使用? 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
的类型,我们也不知道那些 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
的元素类型 的信息。< / p>
(但您可能会注意到我们可以指定任何类型的列表保存列表 ...)
所以要打破它:
// ┌ 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"泛型类型具有不变性,即List<Dog>
is not a subtype of 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
:
List<String>
是List<?>
的子类型,但List<List<String>>
不是 List<List<?>>
的子类型。如前所示,这可以防止我们通过向List
添加错误元素来危及类型安全。List<List<String>>
是<{1}}的子类型,因为有界通配符允许协方差。也就是说,List<? extends List<?>>
允许考虑? extends
是List<String>
的子类型。 List<?>
实际上是一个共享的超类型:
List<? extends List<?>>
List<? extends List<?>>
╱ ╲
List<List<?>> List<List<String>>
仅接受 Map<Integer, List<String>>
作为值。List<String>
接受任何 Map<?, List<?>>
作为值。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
具有相同的元素类型。我们不知道 的类型是什么,但我们可以抽象地使用它。这使我们能够执行“盲目的”#34;操作
例如,这种声明可能对某种积累很有用:
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>>
的类型,那么这是最宽松的声明。
我们也可以使用bounds来嵌套类型参数:
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) {
// ...
}
}
}
及其中的所有内容。
答案 1 :(得分:1)