为什么必须使用泛型类型 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) {}
最佳答案
从根本上说,List<List<?>>
和 List<? extends List<?>>
有不同的类型参数。
实际上,一个是另一个的子类型,但首先让我们详细了解它们各自的含义。
理解语义差异
一般来说,通配符?
代表一些“缺失的信息”。这意味着“这里曾经有一个类型参数,但我们不再知道它是什么”。并且因为我们不知道它是什么,所以对我们如何使用引用该特定类型参数的任何内容施加了限制。
目前,让我们使用 List
来简化示例。而不是 Map
.
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<?>>
.我们不知道是什么类型的 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
的元素类型在里面。(但您可能会注意到,我们可以将任何类型的 List 持有 Lists 分配给它...)
所以要分解它:
// ┌ 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>>
是 List<? extends List<?>>
的子类型,因为有界通配符允许协方差。即,? extends
允许 List<String>
的事实是 List<?>
的子类型被考虑。 List<? extends List<?>>
实际上是一个共享父类(super class)型: 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<?>>
是一个共享父类(super class)型,它施加了安全限制: Map<?, ? extends List<?>>
╱ ╲
Map<?, List<?>> Map<Integer, List<String>>
泛型方法的工作原理
通过在方法上使用类型参数,我们可以断言
List
有一些具体的类型。static <E> void test(Map<?, List<E>> m) {}
此特定声明要求所有
List
s 在 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
因为我们不知道它的 key 类型是什么了。但是,我们可以操纵它的值,因为我们知道它们都是 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
. 关于Java嵌套泛型类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22806202/