在尝试理解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
?
答案 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 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>
内找到任何这些实例。
回想一下,我提到您可能无法在此系列中添加或删除内容?记住这个惯例:
制片人延伸;消费者超级。
请注意,这是从集合的角度来看;如果集合与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
?
没有。该实例中的BookCase
是ArrayList
,而不是Book
。碰巧是数组列表绑定存储书籍的情况,但它本身不 a Book
。
答案 2 :(得分:4)
从文档中查看this。
通常,如果Foo是Bar的子类型(子类或子接口),并且G是一些泛型类型声明,则G不是G的子类型的情况。这可能是您需要学习的最难的事情关于泛型,因为它违背了我们深刻的直觉。
所以基本上当你写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>
或任何可迭代且包含书籍的东西一起使用。