Java泛型不可能赋值?

3

每次我认为自己更好地理解泛型(并可以在不编译的情况下回答问题)时,我都会遇到一个打破这个理论的例子。以下是一个非常简单的例子:

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 的超类型。然而,第二个调用编译通过了。但是假设这是因为编译器可以推断出某种类型,那么它会在运行时失败,对吗?对吗?不对。它能正常工作。有没有人能给我的生活带来一点理智?

嗯?你没有写 List<List<? super String>>,你写的是 List<? super List<String>> - chrylis -cautiouslyoptimistic-
1
编译器可能会将List.of的泛型参数推断为Object...(不确定实际情况是否如此)。 - Sweeper
@Sweeper 是的,那也是我自己的唯一解释... - Eugene
你是如何得出 X extends String implements List 的?编译器只需要找到一个满足 super List<String> 并且可以从 String 转换的类型(即 String 的超类),而 Object 满足这个条件。为什么要使用 X extends String implements List - Sweeper
@Sweeper 没事了,我已编辑好了。但不管怎样,你实际上让我想起了美妙的 --debug=verboseResolution=all (有时很有帮助) 选项... - Eugene
2个回答

9
啊,该死!
javac  --debug=verboseResolution=all Sandbox.java

显示consumer(List.of("123"))被编译为:

instantiated signature: (Object)List<Object>
target-type: List<? super List<String>>

哇,我不知道我可以看到javac如何思考我的源代码!今天学到了新东西! - Sweeper
@Sweeper,这不是一个被记录在文档中的选项。它可能会在任何版本中被更改或删除。不过,你的问题让我想起了这个选项,所以我随机给了你一个赞。 - Eugene
Eugene,--debug=verboseResolution=all 加一分。你可能需要添加一些关于发现的解释。我不确定我能正确地解释它。 - Nikolas Charalambidis

2
如果您想要一个更“实用”的解释,您必须考虑 param 是什么。
通过应用 PECS,可以发现它是一个消费者 List<String>
  • 兼容的消费者是那些可以消费 List<String> 或其任何父类型的消费者;
  • 当应用于 List 时,它意味着您可以使用任何 List<String> 的父类型调用 add()。因此,List<Object> 是兼容的。
这就是为什么可以使用 List<Object> 调用 consumer(),但不能使用 List<String>String 不是 List<String> 的超类型)。
由于在声明时 List.of(…) 总是可以匹配到 List<Object>,因此接受第二个调用。
请注意,在consumer()方法内部,您将无法从param中检索List<String>(即不能将其用作生产者)。您只能向其中添加新内容(即将其用作消费者) - 实际上,您可以将List<String>添加到List<Object>中(尽管在这种特定情况下,List.of()会生成一个不可变列表,因此它会在运行时失败)。

谢谢。我对这个问题有一定了解,我的问题是它所暗示的意思。最终结果证明它是相当简单的。 - Eugene

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接