Java嵌套泛型类型
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22806202/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java nested generic type
提问by Louis Bliss
How come one must use the generic type Map<?, ? extends List<?>>
instead of a simpler Map<?, List<?>>
for the following test()
method?
对于以下方法,为什么必须使用泛型类型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) {}
Noting that the following works, and that the three methods have the same erased type anyways.
请注意以下工作,并且无论如何这三种方法具有相同的擦除类型。
public static <E> void test(Map<?, List<E>> m) {}
采纳答案by Radiodef
Fundamentally, List<List<?>>
and List<? extends List<?>>
have distinct type arguments.
从根本上说,List<List<?>>
并List<? extends List<?>>
具有不同的类型参数。
It's actually the case that one is a subtype of the other, but first let's learn more about what they mean individually.
实际上,一个是另一个的子类型,但首先让我们详细了解它们各自的含义。
Understanding semantic differences
理解语义差异
Generally speaking, the wildcard ?
represents some "missing information". It means "there was a type argument here once, but we don't know what it is anymore". And because we don't know what it is, restrictions are imposed on how we can use anything that refers to that particular type argument.
一般来说,通配符?
代表一些“缺失的信息”。它的意思是“这里曾经有一个类型参数,但我们不再知道它是什么了”。并且因为我们不知道它是什么,所以对我们如何使用引用该特定类型参数的任何内容施加了限制。
For the moment, let's simplify the example by using List
instead of Map
.
目前,让我们使用List
代替 来简化示例Map
。
A
List<List<?>>
holds any kind of List with any type argument. So i.e.: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) );
We can put any
List
intheAnyList
, but by doing so we have lost knowledge of their elements.When we use
? extends
, theList
holds some specific subtype of List, but we don't know what it is anymore. So i.e.: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>() );
It's no longer safe to add anything to the
theNotSureList
, because we don't know the actual type of its elements. (Wasit originally aList<LinkedList<Float>>
? Or aList<Vector<Float>>
? We don't know.)We can put these together and have a
List<? extends List<?>>
. We don't know what type ofList
it has in it anymore, and we don't know the element type of thoseList
s either. So i.e.: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" );
We've lost information bothabout
theReallyNotSureList
, as well asthe element type of theList
s inside it.(But you may note that we can assignany kind of List holding Liststo it...)
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<?>>
. 我们不再知道List
它里面有什么类型,我们也不知道那些List
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的元素类型。(但您可能会注意到,我们可以为它分配任何类型的List 持有 Lists...)
So to break it down:
所以要分解它:
// ┌ applies to the "outer" List
// ▼
List<? extends List<?>>
// ▲
// └ applies to the "inner" List
The Map
works the same way, it just has more type parameters:
在Map
以同样的方式,它只是有更多的类型参数:
// ┌ Map K argument
// │ ┌ Map V argument
// ▼ ▼
Map<?, ? extends List<?>>
// ▲
// └ List E argument
Why ? extends
is necessary
为什么? extends
需要
You may know that "concrete"generic types have invariance, that is, List<Dog>
is not a subtype of List<Animal>
even if class Dog extends Animal
. Instead, the wildcard is how we have covariance, that is, List<Dog>
isa subtype of List<? extends Animal>
.
您可能知道“具体的”泛型类型具有不变性,也就是说,List<Dog>
它不是List<Animal>
if的子类型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;
So applying these ideas to a nested List
:
因此,将这些想法应用于嵌套List
:
List<String>
is a subtype ofList<?>
butList<List<String>>
is nota subtype ofList<List<?>>
. As shown before, this prevents us from compromising type safety by adding wrong elements to theList
.List<List<String>>
isa subtype ofList<? extends List<?>>
, because the bounded wildcard allows covariance. That is,? extends
allows the fact thatList<String>
is a subtype ofList<?>
to be considered.List<? extends List<?>>
is in fact a shared supertype:List<? extends List<?>> ╱ ╲ List<List<?>> List<List<String>>
List<String>
是的一个亚型List<?>
,但List<List<String>>
就是没有一个亚型List<List<?>>
。如前所述,这可以防止我们通过向List
.List<List<String>>
是的子类型List<? extends List<?>>
,因为有界通配符允许协方差。也就是说,? extends
允许考虑List<String>
作为 的子类型的事实List<?>
。List<? extends List<?>>
实际上是一个共享超类型:List<? extends List<?>> ╱ ╲ List<List<?>> List<List<String>>
In review
审核中
Map<Integer, List<String>>
accepts onlyList<String>
as a value.Map<?, List<?>>
accepts anyList
as a value.Map<Integer, List<String>>
andMap<?, List<?>>
are distinct types which have separate semantics.- One cannot be converted to the other, to prevent us from doing modifications in an unsafe way.
Map<?, ? extends List<?>>
is a shared supertype which imposes safe restrictions:Map<?, ? extends List<?>> ╱ ╲ Map<?, List<?>> Map<Integer, List<String>>
Map<Integer, List<String>>
接受只List<String>
作为值。Map<?, List<?>>
接受anyList
作为值。Map<Integer, List<String>>
和Map<?, List<?>>
是具有不同语义的不同类型。- 一个不能转换为另一个,以防止我们以不安全的方式进行修改。
Map<?, ? extends List<?>>
是一个共享超类型,它施加了安全限制:Map<?, ? extends List<?>> ╱ ╲ Map<?, List<?>> Map<Integer, List<String>>
How the generic method works
泛型方法的工作原理
By using a type parameter on the method, we can assert that List
has some concrete type.
通过在方法上使用类型参数,我们可以断言它List
具有某种具体类型。
static <E> void test(Map<?, List<E>> m) {}
This particular declaration requires that allList
s in the Map
have the same element type. We don't know what that type actually is, but we can use it in an abstract manner. This allows us to perform "blind" operations.
此特定声明要求 中的所有List
sMap
具有相同的元素类型。我们不知道那个类型究竟是什么,但我们可以抽象地使用它。这允许我们执行“盲”操作。
For example, this kind of declaration might be useful for some kind of accumulation:
例如,这种声明可能对某种累积有用:
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;
}
We can't call put
on m
because we don't know what its key typeis anymore. However, we can manipulate its valuesbecause we understand they are all List
with the same element type.
我们无法调用put
,m
因为我们不再知道它的密钥类型是什么。但是,我们可以操纵它的值,因为我们知道它们都List
具有相同的元素类型。
Just for kicks
只是为了踢
Another option which the question does not discuss is to have both a bounded wildcard and a generic type for the List
:
问题未讨论的另一个选项是同时具有有界通配符和泛型类型List
:
static <E> void test(Map<?, ? extends List<E>> m) {}
We would be able to call it with something like a Map<Integer, ArrayList<String>>
. This is the most permissive declaration, if we only cared about the type of E
.
我们可以用类似的东西来调用它Map<Integer, ArrayList<String>>
。如果我们只关心E
.
We can also use bounds to nest type parameters:
我们还可以使用边界来嵌套类型参数:
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) {
// ...
}
}
}
This is both permissive about what we can pass to it, as well as permissive about how we can manipulate m
and everything in it.
这既允许我们可以传递给它的内容,也允许我们如何操作m
以及其中的所有内容。
See also
也可以看看
- "Java Generics: What is PECS?"for the difference between
? extends
and? super
. - JLS 4.10.2. Subtyping among Class and Interface Typesand JLS 4.5.1. Type Arguments of Parameterized Typesfor entry points to the technical details of this answer.
- “Java 泛型:什么是 PECS?”
? extends
和之间的区别? super
。 - JLS 4.10.2。类和接口类型和JLS 4.5.1之间的子类型。参数化类型的类型参数作为该答案的技术细节的入口点。
回答by Bartek Maraszek
This is because the subclassing rules for generics are slightly different from what you may expect. In particular if you have:
这是因为泛型的子类化规则与您所期望的略有不同。特别是如果您有:
class A{}
class B extends A{}
then
然后
List<B>
is not a subclass of List<A>
List<B>
不是的子类 List<A>
It's explained in details hereand the usage of the wildcard (the "?" character) is explained here.