收集<! - ?扩展T - > vs Collection <t> </t>

时间:2015-02-11 21:14:16

标签: java syntax extends

在尝试理解Spring MVC处的概念后,我遇到了我从未见过的表达式Collection<? extends Book>。我试图自己解决这个问题,但我发现使用Collection<? extends Book>Collection<Book>没有区别。我猜它只允许Book的扩展,但它确实允许Book。抓那个。我曾尝试使用谷歌,但从那以后?是google中的通配符,它​​几乎不可能搜索。我已经在stackoverflow中搜索了答案,但是有关此问题的所有问题(例如List<? extends MyType><? 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

5 个答案:

答案 0 :(得分:5)

考虑以下

class Animal { }
class Horse extends Animal { }

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

如果没有extends语法,您只能使用签名

中的确切类
    specific(new ArrayList<Horse>()); // <== compiler error

使用通配符扩展,您可以允许Animal的任何子类

    wildcard(new ArrayList<Horse>());  // <== OK

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

答案 1 :(得分:5)

Collection<Book>Collection<? extends Book>代表一个可以容纳Book个实例的集合,以及可以被认为是Book对象是一个关系。此属性也扩展到接口。

这里的区别在于后一种形式被视为有界。根据绑定,您将无法在此集合中添加或删除元素。

? extends Tupper 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>内找到任何这些实例。

回想一下,我提到您可能无法在此系列中添加或删除内容?记住这个惯例:

  

制片人延伸;消费者超级。

请注意,这是从集合的角度来看;如果集合与extends有界,那么它就是生产者;如果它与super绑定,则它是消费者。

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

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绑定,则您只打算元素插入该集合。

  

后续问题:BookCase extends ArrayList<Book>是否表示BookCase extends Book

没有。该实例中的BookCaseArrayList,而不是Book。碰巧是数组列表绑定存储书籍的情况,但它本身 a Book

答案 2 :(得分:4)

从文档中查看this

  

通常,如果Foo是Bar的子类型(子类或子接口),并且G是一些泛型类型声明,则G不是G的子类型的情况。这可能是您需要学习的最难的事情关于泛型,因为它违背了我们深刻的直觉。

看看next page about wildcards

所以基本上当你写void doStuff(List<Book>){}时,你只能对Book个对象列表进行处理。

Novel s, Magazine s, ComicBook s。

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

答案 3 :(得分:2)

以下是清楚显示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);
    }
}

该代码编译。如果将BookCase的构造函数参数更改为Collection<Book>,则此示例不会编译。

答案 4 :(得分:0)

List<Book>是一个可能包含任何图书的列表。由于书的种类没有限制,您可以添加任何书籍。

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

考虑一种方法,该方法采用书籍列表并返回具有最多页面的书籍。

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

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

如果您将签名更改为

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

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