虽然类似问题有this和this;那些不回答两种通用声明方式之间的区别是什么?为什么其中一种方法会编译而其他方式不在以下示例中? 不编译的那个基于official tutorial和我一样。
public void processMap1(Map<String, List<?>> map) {
}
public <T> void processMap2(Map<String, List<T>> map) {
}
public void f() {
Map<String, List<String>> map = new HashMap<>();
processMap1(map); // ERROR
processMap2(map); // OK
}
我希望我的方法同时适用于Map<String, List<String>>
和Map<String, List<Object>>
答案 0 :(得分:3)
您有一个方法需要X
类型的对象。为了将对象传递给此方法,此对象的类型Y
必须是X
的子类型。
对于简单的情况,这可能是显而易见的:
void processNumber(Number number) { ... }
void callIt()
{
Integer integer = null;
processNumber(integer); // Fine. Integer is a subtype of Number
String string = null;
processNumber(string); // Error. String is NOT a subtype of Number
}
到目前为止,这么好。但是现在你进入参数化类型的混淆的子类型关系世界。 Angelika Langer在她的泛型常见问题解答中详细介绍了Which super-subtype relationships exist among instantiations of generic types?:
这相当复杂,类型关系最好根据此FAQ条目中的说明设置表格。
所以我甚至不想尝试在这里重现这个,但只是试图解释(简化)为什么第二个版本有效,但第一个版本没有。
第二个版本工作的事实可以用编译器执行的类型推断来解释。基本上,它只是查看参数,看到它是Map<String, List<String>>
,并推断方法声明的T
必须为String
才能使其生效。你可以想象这只是T
被String
替换,然后方法完全匹配。 (它不是那么容易,但可以直观地理解为这样)
为什么第一个版本不工作的原因看似简单。回到初始语句:该方法需要一个对象具有方法签名中声明的类型的子类型。关键是:
Map<String, List<String>>
不是Map<String, List<?>>
这个事实(在这种情况下)甚至可以通过省略方法调用来简化:
Map<String, List<String>> map = new HashMap<>();
Map<String, List<?>> otherMap = map; // Does not work
再次,我将留下&#34;类型理论的细节&#34;其后面是上面链接的FAQ条目,但至少解释了为什么不允许这样做:Map<String, List<?>>
是一个将字符串映射到包含未知/未指定类型的列表的映射。因此,processMap1
方法的以下实现将是有效的:
public void processMap1(Map<String, List<?>> map)
{
List<Number> numbers = new ArrayList<Number>();
map.put("x", numbers);
}
想象一下,用Map<String, List<String>>
调用此方法是有效的:
public void f() {
Map<String, List<String>> map = new HashMap<>();
processMap1(map); // Imagine this was possible
List<String> shouldBeStrings = map.get("x");
}
然后您迟早会得到ClassCastException
,因为您将Map<String, List<String>>
传递给该方法,但该方法可以将List<Number>
放入此地图中。
或简称:不允许这样做,因为它不是类型安全的。
编辑以回应评论(这有点超出原始问题,所以我会尽量保持简短):
对于第二种情况,没有T
来捕获任何类型。类型为?
,直观地说,它可以代表任何类型。对于从reverse
到rev
建议的列表调用,T
仍为?
,但可用于专门代表类型的列表中的元素。或者再次提到子类型关系:
List<T>
总是 List<?>
的子类型(无论T
是什么)
从processMap1
到processMap2
的建议来电不会出于上述原因:即使Y
是[{1}}的子类型,类型{{1}也是如此} 不是X
的子类型。在这里,this question可能是相关的。