为什么这段Java代码片段可以编译通过?

4
1.1 例子
import java.util.*;

public class LOLUnknowns1 {
    static void probablyIllegal(List<List<?>> lol, List<?> list) {
        lol.add(list); // this compiles just fine
    }
}

怎么可能这个1.1的代码能编译通过呢?我们最后可能会有一个List> lol和一个List list。
以下是代码片段: 1.2 示例
import java.util.*;

public class TwoListsOfUnknowns2 {
    static void doSomethingIllegal(List<?> list1, List<?> list2) {
        list1.addAll(list2); // DOES NOT COMPILE as expected!!!
            
    }
}

1.1个例子让我困惑,怎么可能顺利编译?

我想知道为什么代码能够在预期无法编译的情况下成功编译,希望了解具体原因。


你是否遇到了未经检查的类型转换异常?在第二种情况下,你无法向List<?>中添加元素,因为你不知道?代表的是什么。而在第一种情况下,你知道你有一个List。在类型擦除后,List<?>仍然只是一个List。 - matt
3个回答

2
怎么可能这个1.1的代码能编译通过呢?我们最后可能会有一个`List> lol`和一个`List list`?
你不能将一个`List>`传递给类型为`List>`的参数。如果你尝试这样做:
probablyIllegal(new ArrayList<List<Integer>>(), new ArrayList<String>());

你得到

类型LOLUnknowns1中的方法probablyIllegal(List<List<?>>, List<?>)对于参数(ArrayList<List<Integer>>, ArrayList<String>)不适用。

原因是尽管List<Integer>List<?>的子类型,但List<List<Integer>>并不是List<List<?>>的子类型,就像尽管StringObject的子类型,List<String>也不是List<Object>的子类型。

如果你想传递一个List<List<Integer>>,你需要声明参数的类型为List<? extends List<?>>

static void probablyIllegal(List<? extends List<?>> lol, List<?> list) {
    lol.add(list); 
}

改变之后,编译器将接受你的调用,但拒绝你的方法实现:

类型中的方法add(capture#1-of ? extends List<?>)在类型List<capture#1-of ? extends List<?>>中不适用于参数(List<capture#2-of ?>)

错误信息有点晦涩(你需要阅读通配符捕获相关知识才能理解),但基本上它表明你不能将List<?>添加到List<? extends List<?>>,因为你不知道lol包含的元素类型,因此也无法确定list是否具有兼容的类型。
总结一下,你可以为List指定特定的元素类型,这样你就可以add,但不能使用不同类型的List,或者你可以将List的元素类型保持未指定状态,这样你就可以使用任何List,但不能向其添加任何内容。无论哪种方式,都不会发生任何不好的事情。

0
只要在带有通配符的泛型声明中不使用"extends"或"super",编译器就无法在运行时决定这是否有效,因为只有一个"?"告诉它:"我想接受任何东西",而generic declaration with wildcardsadd(E e)也是如此。
你尝试过将具体的List对象作为lollistlist1list2参数传递给代码并运行吗?
错误(在Eclipse中)是:

The method addAll(Collection<? extends capture#1-of ?>) in the type List<capture#1-of ?> is not applicable for the arguments (List<capture#2-of ?>)

所以,有两种不同的通配符捕获。一个是Collection<?>(#1;由addAll(Collection<? extends E> c))/ List<?> list1,另一个是List<?> list2(#2)。
现在,请参阅上面第一个链接下的文章:

然而,Object的集合不是任何集合的超类型。

例如,List<Object>不是List<String>的超类型,[...]

你声明 list1 是一个任意类型 (?1) 的 List,因此 addAll() 期望的是扩展了任意类型 (E = ?1) 的某个东西 (?2),但是根据引文所示,泛型并不是这样工作的。

0
我认为这就是你在找的东西。
简单总结一下,对于第二个例子,如果你想要一个包含多种类型的列表,可以使用上界通配符来实现。
在你的第一个例子中,有一个额外的包装器,即 List<?>,这就是它能够工作的原因,最终使用了 List 接口的一个方法。

您的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认您的回答是否正确。您可以在帮助中心找到关于如何撰写好回答的更多信息。 - Community
你的回答可以通过提供更多的支持性信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的回答是否正确。你可以在帮助中心找到关于如何撰写好的回答的更多信息。 - undefined

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