[英]Java nested generic type
为什么必须使用泛型类型Map<?, ? extends List<?>>
对于以下test()
方法Map<?, List<?>>
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) {}
请注意以下工作,并且无论如何这三种方法具有相同的擦除类型。
public static <E> void test(Map<?, List<E>> m) {}
从根本上说, List<List<?>>
和List<? extends List<?>>
List<? extends List<?>>
具有不同的类型参数。
实际上,一个是另一个的子类型,但首先让我们详细了解它们各自的含义。
一般来说,通配符?
代表一些“缺失的信息”。 它的意思是“这里曾经有一个类型参数,但我们不再知道它是什么了” 。 并且因为我们不知道它是什么,所以对我们如何使用引用该特定类型参数的任何内容施加了限制。
目前,让我们使用List
而不是Map
来简化示例。
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
? 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<? 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
? extends
是必要的您可能知道“具体”泛型类型具有不变性,即List<Dog>
不是List<Animal>
的子类型,即使class Dog extends Animal
。 相反,通配符是我们如何获得协方差,即List<Dog>
是List<? extends Animal>
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<?>>
List<? extends List<?>>
,因为有界通配符允许协方差。 也就是说, ? extends
? extends
允许考虑List<String>
是List<?>
的子类型这一事实。 List<? extends 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<?, ? extends List<?>> ╱ ╲ Map<?, List<?>> Map<Integer, List<String>>
通过在方法上使用类型参数,我们可以断言List
具有某种具体类型。
static <E> void test(Map<?, List<E>> m) {}
这个特殊的声明要求Map
中的所有List
都具有相同的元素类型。 我们不知道那个类型究竟是什么,但我们可以抽象地使用它。 这允许我们执行“盲”操作。
例如,这种声明可能对某种累积有用:
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
on 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
? extends
和? super
? super
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.