使用"<? extends SomeObject>"而不是"<SomeObject>"有什么用处?

39

所以我正在查看一些Java代码,然后偶然发现了:

List<? extends SomeObject> l;

基本上,此列表接受所有类型为SomeObject的对象- SomeObject本身或其子类。但根据多态性,它的子类也可以被视为SomeObject,因此这也可以起作用:

List<SomeObject> l;

那么既然第二种选项已经清晰明确且几乎相同,为什么有人会使用第一种选项呢?


7
这个在Java教程中有很好的说明:http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html - Oliver Charlesworth
5个回答

37
List<SomeObject> l;
在这种情况下,你不能说 List<SomeObject> l = new ArrayList<SubClassOfSomeObjectClass>;不允许),而是要写为

List<? extends SomeObject> l;

你可以这样说:

List<? extends SomeObject> l = new ArrayList<SubClassOfSomeObject>;(允许的)

但请注意,在 List<? extends SomeObject> l = new ArrayList<SubClassOfSomeObject>; 中,你不能向列表l中添加任何内容,因为 ? 代表未知的类(当然可以添加 null 元素)。

更新:对于您在评论中提出的问题“如果我不能向列表中添加任何内容,我可能会做什么?"

现在考虑这样一种情况,你需要编写一个函数来打印你的列表,但它必须仅接受具有某些对象子类的 List。在这种情况下,如我之前所述,你无法使用

public void printList(List<SubClassOfSomeObjectClass> someList)

那么你会怎么做呢?你会做类似这样的事情:

    public void printList(List<? extends SomeObject> someList) {
        for(SomeObject myObj : someList) {
             //process read operations on  myObj
        }

1
请你澄清两件事情: 如果我不能向列表中添加任何内容,那么我可能会做什么? 在构造函数内部声明的<>中的类的含义是什么? - Or Cyngiser
请参考以下两个链接(通配符:非常详细地解释)http://docs.oracle.com/javase/tutorial/extra/generics/subtype.html和http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html。 - Aniket Thakur
@OrCyngiser “如果我不能向列表中添加任何内容,我还能做什么?” 有时您只想读取/使用某个容器的内容而不进行修改。 “构造函数中<>中声明的类是什么意思?” 不确定您在问什么。简单来说,它是泛型类型。更多信息请参见此处。如果您询问“<>”,请查看此处此处 - Pshemo
@OrCyngiser 看看更新后的答案。希望有所帮助!还要查看Pshemo在上面评论中提到的链接。这将使您更好地理解。 - Aniket Thakur
一个注意事项:像SonarQube这样的代码分析检查器会将返回通配符泛型的方法标记为关键的“代码异味”:“强烈建议不要使用通配符类型作为返回类型。因为类型推断规则相当复杂,使用该API的用户很可能不知道如何正确使用它。” https://rules.sonarsource.com/java/RSPEC-1452 - michaelok

4
您想要阅读的关键链接是http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html,该链接详细解释了通配符泛型。

List<SomeObject>List<? extends SomeObject>不同。请看下面的例子:

List<Object> x = new ArrayList<Object>();
List<String> y = new ArrayList<String>();
// x = y; Will throw Compilation exception
List<? extends Object> z = y; //will pass compilation

您可能需要注意,您可以将字符串添加到x和y列表中,但是当您编写库函数(例如在链接中显示的示例中的printCollection)时,这将非常有用,而不是接受 Collection<Object>,在这种情况下,用户无法将他的字符串列表传递给您的方法。如果您接受 Collection<? extends Object>,则用户可以传递他的 Collection<Apple>,Collection<Orange>等,而不必显式创建另一个列表。

2

List<? extends SomeObject> l; 不接受 new SomeObject(), 但是 List<SomeObject> l; 可以。

以下也不起作用: List<SomeObject> l = new ArrayList<SubTypeOfSomeObject>()

以下可以正常工作: List<? extends SomeObject> l = new ArrayList<SubTypeOfSomeObject>()


0

X 即使可以转换为 Y,也不能添加到 List<Y> 中。

因此,在您的第二种情况中,如果允许向 List<X> l; 添加 X 的子类,那么这将违反类型安全的基本原则。


-2

作为第一个答案,

List<? extends SomeObject> l;

必须包含从SomeObject继承的对象,而不是直接使用SomeObject。


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