英文:
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).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论