问题描述
每当我思考时,我都会更好地理解泛型(并且无需编译即可回答),我得到了一个打破这一理论的例子。这是一个非常简单的示例:
static void consumer(List<? super List<String>> param) {
System.out.println(param);
}
和两个调用:
public static void main(String[] args) {
List<String> list = List.of("123");
consumer(list);
consumer(List.of("123"));
}
对我来说,任何调用都不应该编译。 String
不是List
的超类型。仍然,第二个编译。但是,我们假设发生这种情况是因为编译器可以在此处推断 some 类型。当然,这种类型不存在,它将在运行时失败,对吗?对?不。它就是有效的。因此,有人可以给我的生活带来些理智吗?
解决方法
该死!
javac --debug=verboseResolution=all Sandbox.java
表明consumer(List.of("123"))
已编译为:
instantiated signature: (Object)List<Object>
target-type: List<? super List<String>>
,
如果您想要更多的“ 功能”说明,则必须考虑param
是什么。
通过应用PECS,请注意,因此List
是List<String>
的使用者。
- 兼容的使用者是可以使用
List<String>
或其任何父类型的使用者; - 应用于
List
时,表示您可以使用任何父类型为add()
调用List<String>
的列表。因此List<Object>
是兼容的。
这就是为什么可以用consumer()
而不是List<Object>
来调用List<String>
的原因(String
不是List<String>
的超类型)。
由于List.of(…)
在声明时始终可以与List<Object>
匹配,因此它接受第二个调用。
请注意,从consumer()
方法内部,您将永远无法从List<String>
中检索param
(即,将其用作生产者)。您只能在其中添加新的(即,将其用作使用者)–实际上,您可以在List<String>
中添加List<Object>
(尽管在这种情况下,List.of()
会产生一个不可变的列表,因此它将在运行时失败。