Lou*_*iss 25 java generics bounded-wildcard unbounded-wildcard
为什么必须使用泛型类型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) {}
Run Code Online (Sandbox Code Playgroud)
注意到以下工作,并且三种方法无论如何都具有相同的擦除类型.
public static <E> void test(Map<?, List<E>> m) {}
Run Code Online (Sandbox Code Playgroud)
Rad*_*def 52
从根本上说,List<List<?>>并且List<? extends List<?>>具有不同的类型参数.
事实上,一个是另一个的子类型,但首先让我们更多地了解它们的含义.
一般来说,通配符?代表一些"缺失的信息".这意味着"这里曾经存在类型论证,但我们不知道它是什么".而且因为我们不知道它是什么,所以对如何使用引用该特定类型参数的任何东西施加限制.
暂时,让我们使用List而不是简化示例Map.
A List<List<?>>包含任何类型参数的任何类型的List.所以ie:
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) );
Run Code Online (Sandbox Code Playgroud)
我们可以把任何List的theAnyList,但这样做,我们已经失去了的知识,它们的元素.
当我们使用时? extends,List保存List的某个特定子类型,但我们不知道它是什么.所以ie:
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>() );
Run Code Online (Sandbox Code Playgroud)
添加任何内容不再安全theNotSureList,因为我们不知道其元素的实际类型.(当时它最初是一个List<LinkedList<Float>>?还是List<Vector<Float>>?我们不知道.)
我们可以将这些放在一起并有一个List<? extends List<?>>.我们不知道是什么类型的List.它有它了,我们不知道的元素类型的 List小号两种.所以ie:
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" );
Run Code Online (Sandbox Code Playgroud)
我们已经丢失了有关它内部信息的信息theReallyNotSureList,以及它的元素类型List.
(但你可能会注意到我们可以为它分配任何类型的List保持列表 ...)
所以要打破它:
// ? applies to the "outer" List
// ?
List<? extends List<?>>
// ?
// ? applies to the "inner" List
Run Code Online (Sandbox Code Playgroud)
该Map作品以同样的方式,它只是有更多的类型参数:
// ? Map K argument
// ? ? Map V argument
// ? ?
Map<?, ? extends List<?>>
// ?
// ? List E argument
Run Code Online (Sandbox Code Playgroud)
? extends有必要您可能知道"具体"泛型类型具有不变性,即即使List<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;
Run Code Online (Sandbox Code Playgroud)
所以将这些想法应用于嵌套List:
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>>
Run Code Online (Sandbox Code Playgroud)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>>
Run Code Online (Sandbox Code Playgroud)通过在方法上使用类型参数,我们可以断言它List具有一些具体类型.
static <E> void test(Map<?, List<E>> m) {}
Run Code Online (Sandbox Code Playgroud)
这个特殊的声明要求所有的 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;
}
Run Code Online (Sandbox Code Playgroud)
我们不能打电话put,m因为我们不知道它的关键类型是什么.但是,我们可以操纵它的值,因为我们知道它们都List具有相同的元素类型.
问题没有讨论的另一个选择是同时具有有界通配符和泛型类型List:
static <E> void test(Map<?, ? extends List<E>> m) {}
Run Code Online (Sandbox Code Playgroud)
我们可以用类似的东西来调用它Map<Integer, ArrayList<String>>.如果我们只关心它的类型,这是最宽容的声明E.
我们也可以使用bounds来嵌套类型参数:
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) {
// ...
}
}
}
Run Code Online (Sandbox Code Playgroud)
这既允许我们传递给它的东西,也允许我们如何操纵m它以及它中的所有东西.
? extends和? super.