英文:
Java generics impossible assignment?
问题
每次我认为我对泛型有了更好的理解(可以不编译就能回答问题),我就会遇到一个打破这一理论的示例。以下是一个非常简单的示例:
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 的超类型。然而,第二个调用通过了编译。但是让我们假设这是因为编译器可以推断出某种类型。当然,这样的类型是不存在的,而且它会在运行时失败,对吗?对吗?不对。它居然可以正常工作。因此,有人可以给我的生活带来一些理智吗?
英文:
Every time I think I understand generics better (and can answer without compiling), I get to an example where this theory breaks. Here is a very simple example:
static void consumer(List<? super List<String>> param) {
    System.out.println(param);
}
And two invocations:
public static void main(String[] args) {
    List<String> list = List.of("123");
    consumer(list);
    consumer(List.of("123"));
}
To me, none of the invocations should compile. A String is not a supertype of List. Still, the second one compiles. But let's suppose that this happens because the compiler could infer some type here. Of course such a type does not exist and it will fail at runtime, right? Right? Nope. It just works. As such, can someone bring some sanity to my life please?
答案1
得分: 9
啊,该死!
javac --debug=verboseResolution=all Sandbox.java
显示consumer(List.of("123"))被编译为:
实例化签名:(Object)List
英文:
Ah darn!
javac  --debug=verboseResolution=all Sandbox.java
shows that consumer(List.of("123")) is compiled to:
instantiated signature: (Object)List<Object>
target-type: List<? super List<String>>
答案2
得分: 2
如果您想要一个更加 "功能性" 的解释,您必须考虑一下 param 是什么。
通过应用 PECS,注意它因此是一个消费者类型为 List<String> 的 List。
- 兼容的消费者是那些可以消费 
List<String>或其任何父类型的消费者; - 当应用于 
List时,它意味着您可以对其调用带有List<String>任何父类型的add()。因此,List<Object>是兼容的。 
这就是为什么 consumer() 可以用 List<Object> 调用,但不能用 List<String> 调用(因为 String 不是 List<String> 的超类型)。
由于 List.of(…) 在声明时总是可以与 List<Object> 匹配,因此它接受第二个调用。
请注意,在 consumer() 方法内部,您将永远无法从 param 中检索到 List<String>(即无法将其用作生产者)。您只能将新元素添加到其中(即将其用作消费者) - 的确,您可以将 List<String> 添加到 List<Object> 中(尽管在这种特殊情况下,List.of() 生成的是不可变列表,因此在运行时会失败)。
英文:
If you want a more “functional” explanation, you have to consider what param is.
By applying PECS, notice that it is thus a List that is a consumer of List<String>.
- Compatible consumers are those which can consume 
List<String>or any of its parent types; - When applied to 
List, it means a list on which you can calladd()with any parent type ofList<String>. AList<Object>is thus compatible. 
This is why consumer() can be called with a List<Object> but not a List<String> (String is not a supertype of List<String>).
Since List.of(…) can always match List<Object> at declaration time, it accepts the second call.
Note that from inside the consumer() method, you will never be able to retrieve a List<String> from param (i.e. use it as a producer). You can only add new ones into it (i.e. use it as a consumer) – and indeed, you can add a List<String> into a List<Object> (although in this particular case, List.of() produces an immutable list, so it will fail at runtime).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论