Collection<?扩展 T> vs Collection<T>

Collection<? extends T> vs Collection<T>

在尝试理解 Spring MVC, I came across the expression Collection<? extends Book> which I have never seen before. I have tried to figure it out on my own, but I am seeing no difference between using Collection<? extends Book> and Collection<Book>. I was guessing that it only allowed for extensions of Book, but it does allow for Book as well. So scratch that. I have tried using Google, but since ? is a wildcard in google, it makes it nearly impossible to search for. I have searched Whosebug for the answer, but all questions about this (such as List<? extends MyType> and <? extends > Java syntax 中的概念之后)已经假设了 Collection<? extends T> 的知识。这是最初引起我兴趣的代码:

import java.util.ArrayList;
import java.util.Collection;

public class Book {
    public static void main(String[] args) {
        BookCase bookCase1 = new BookCase();
        BookCase bookCase2 = new BookCase(bookCase1);
    }
}

class BookCase extends ArrayList<Book> {
    public BookCase() {
    }

    //acts same as public BookCase(Collection<Book> c) {
    public BookCase(Collection<? extends Book> c) {
        super(c);
    }
}

<? extends T> 是做什么的?它与 <T> 有何不同?

编辑:

后续问题:BookCase extends ArrayList<Book> 是否意味着 BookCase 扩展了 Book

下面的示例清楚地显示了 Collection<? extends Book>Collection<Book> 之间的区别:

public class Test {
    public static void main(String[] args) {
        BookCase bookCase1 = new BookCase();
        BookCase bookCase2 = new BookCase(bookCase1);
        List<FictionBook> fictionBooks = new ArrayList<>();
        BookCase bookCase3 = new BookCase(fictionBooks);
    }
}

class Book {}
class FictionBook extends Book {}

class BookCase extends ArrayList<Book> {
    public BookCase() {
    }

    //does not act same as public BookCase(Collection<Book> c) {
    public BookCase(Collection<? extends Book> c) {
        super(c);
    }
}

该代码编译。如果将 BookCases 构造函数参数更改为 Collection<Book>,则此示例将无法编译。

从文档中查看 this

In general, if Foo is a subtype (subclass or subinterface) of Bar, and G is some generic type declaration, it is not the case that G is a subtype of G. This is probably the hardest thing you need to learn about generics, because it goes against our deeply held intuitions.

也看看 next page about wildcards

所以基本上当你写 void doStuff(List<Book>){} 时,你只能对 Book 对象的列表做一些事情。

Novels, Magazines, ComicBooks。

同样,这是因为虽然 Novel 扩展了 Book,但 G<Novel> 实际上并没有扩展 G<Book>。它不是很直观,但是文档中的示例将帮助您看到它。

考虑以下因素

class Animal { }
class Horse extends Animal { }

private static void specific(List<Animal> param) { }
private static void wildcard(List<? extends Animal> param) { }

如果没有扩展语法,您只能在签名中使用准确的 class

    specific(new ArrayList<Horse>()); // <== compiler error

通过通配符扩展,您可以允许 Animal

的任何子classes
    wildcard(new ArrayList<Horse>());  // <== OK

通常使用 ?扩展语法,因为它使您的代码更具可重用性和面向未来。

Collection<Book>Collection<? extends Book>都代表一个collection可以容纳Book个实例和objects可以被认为是Book 通过 is-a 关系。这 属性 也扩展到接口。

这里的区别在于后一种形式被认为是有界。根据边界,您将无法添加或删除此 collection.

中的元素

? extends T 是一个 upper bounded wildcard。实际上,它描述了低端的某种类型 (?) 和高端的 Book 之间的层次界限。它是包容性的,因此您可以在其中存储 Book 的实例,但是如果您有其他 类 和接口,例如:

class Novella extends Book {}
class Magazine extends Book {}
class ComicBook extends Book{}
class Manga extends Magazine {}
class Dictionary extends Book {}
class ForeignLanguageDictionary<T extends Language> extends Dictionary {}
interface Language {}

...您可以在 Collection<? extends Book>.

中找到任何这些实例

还记得我提到过您可能无法在此 collection 中添加或删除内容吗?记住这个约定:

Producer extends; consumer super.

注意这是从collection的角度来看的;如果 collection 以 extends 为界,则它是生产者;如果它以 super 为界,则它是一个消费者。

也就是说,从这个collection的角度来看,它已经产生了所有的记录,所以你不能再添加新的记录了。

List<? extends Book> books = new ArrayList<>(); // permanently empty
books.add(new Book()); // illegal

如果您将它与 ? super Book 绑定,您将无法以理智的方式从中检索元素 - 您必须将它们检索为 Object,这不够简洁。

List<? super Book> books = new ArrayList<>();
books.add(new Book());
books.add(new Manga());
Book book = books.get(0); // can't guarantee what Book I get back

主要是,如果您受 ? super T 约束,您只打算 元素插入 collection。

Followup question: Does BookCase extends ArrayList<Book> mean that BookCase extends Book?

没有。 BookCase 在这种情况下是 ArrayList,而不是 Book。恰好数组列表是绑定存储书籍的,但它本身不是一个Book.

List<Book> 是一个可能包含任何书籍的列表。因为没有书的种类限制,可以往里面加什么书。

List<? extends Book> 也是一个包含书籍的列表,但可能还有更多限制。也许实际上它是一个 List<PoetryBook> 并且它可能只存储 PoetryBook 的实例,你只能确定这个列表中的每个项目都是一本书。由于此可能的限制,您不能向此列表添加任何项目。

考虑一种获取图书列表和 returns 页数最多的图书的方法。

Book getBookWithMostPages(List<? extends Book>) {
   ...
}

此方法可以用 List<Book>List<PoetryBook> 调用。

如果您将签名更改为

Book getBookWithMostPages(Iterable<? extends Book>) {
   ...
}

那么它也可以与 Set<ScienceBook> 或任何可迭代且包含书籍的东西一起使用。