2015-02-11 21 views
6

Sau khi cố gắng hiểu các khái niệm tại Spring MVC, tôi đã xem qua biểu thức Collection<? extends Book> mà tôi chưa từng thấy trước đây. Tôi đã cố gắng tìm ra nó một mình, nhưng tôi thấy không có sự khác biệt giữa việc sử dụng Collection<? extends Book>Collection<Book>. Tôi đoán rằng nó chỉ cho phép mở rộng Sách, nhưng nó cũng cho phép Sách. Vì vậy, đầu đó. Tôi đã thử sử dụng Google, nhưng kể từ đó? là một ký tự đại diện trong google, nó làm cho nó gần như không thể tìm kiếm. Tôi đã tìm kiếm stackoverflow cho câu trả lời, nhưng tất cả các câu hỏi về điều này (chẳng hạn như List<? extends MyType><? extends > Java syntax) đã giả định kiến ​​thức của Collection<? extends T>. Dưới đây là đoạn code mà đã bước đầu hấp dẫn sự quan tâm của tôi:Bộ sưu tập <? extends T> vs Bộ sưu tập <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); 
    } 
} 

không <? extends T> làm gì? Nó khác với <T> như thế nào?

EDIT:

followup câu hỏi: Liệu BookCase extends ArrayList<Book> nghĩa là BookCase kéo dài Book?

+2

Một 'Bộ sưu tập 'là một' Bộ sưu tập 'nhưng không phải là 'Bộ sưu tập '. – immibis

+0

Lần như thế này, tôi ước tôi có thể đánh dấu chọn nhiều câu trả lời. Cụ thể là câu trả lời của Makoto và Adam. Trong khi tất cả các câu trả lời đều hữu ích, những câu trả lời cụ thể đã giúp tôi hiểu nó. Vì vậy, nếu bạn đang tìm kiếm câu trả lời cho câu hỏi này, tôi khuyên bạn nên đọc cả hai câu hỏi đó ít nhất, bao gồm cả nhận xét. Cảm ơn mọi người!! – Evorlor

Trả lời

3

Hãy xem xét những điều sau

class Animal { } 
class Horse extends Animal { } 

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

Nếu không có sự mở rộng cú pháp bạn chỉ có thể sử dụng lớp chính xác trong chữ ký

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

Với wildcard kéo dài bạn có thể cho phép bất kỳ lớp con Thú

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

Nói chung tốt hơn nên sử dụng? mở rộng cú pháp vì nó làm cho mã của bạn có thể sử dụng lại nhiều hơn và được chứng minh trong tương lai.

+1

Nhưng một con ngựa là một con vật, và cụ thể() chấp nhận một danh sách các loài động vật ... vậy tại sao một danh sách các con ngựa không được chấp nhận? – Evorlor

+0

Generics không hoạt động giống như chữ ký phương thức chuẩn, có foo (Animal animal) có thể lấy bất kỳ loại Animal nào. – Adam

+1

@Evorlor vì nếu bạn chỉ định tham số kiểu là một số đối tượng, trình biên dịch muốn nó là đối tượng chính xác –

2

Đây là ví dụ cho thấy rõ sự khác biệt giữa 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); 
    } 
} 

mã biên dịch. Nếu bạn thay đổi thông số hàm tạo của BookCase s thành Collection<Book>, ví dụ này sẽ không biên dịch.

4

Khám phá this từ tài liệu.

Nói chung, nếu Foo là một kiểu con (phân lớp hoặc subinterface) của Bar, và G là một số kiểu khai báo chung, không phải là trường hợp G là kiểu con của G. Đây có lẽ là điều khó nhất bạn cần phải tìm hiểu về generics, bởi vì nó đi ngược lại trực giác sâu sắc của chúng tôi.

Hãy xem qua số next page about wildcards.

Vì vậy, về cơ bản khi bạn viết void doStuff(List<Book>){} bạn chỉ có thể thực hiện công cụ vào danh sách chỉ Book đối tượng.

KhôngNovel s, khôngMagazine s, khôngComicBook s.

Một lần nữa, điều này là bởi vì mặc dù Novel kéo dài Book, G<Novel>không thực sự mở rộngG<Book>. Nó không phải là rất trực quan, nhưng ví dụ trong tài liệu sẽ giúp bạn nhìn thấy nó.

3

Cả Collection<Book>Collection<? extends Book> đại diện cho một bộ sưu tập mà có thể giữ Book trường và đối tượng có thể được coi là một Book qua là-một mối quan hệ. Thuộc tính này cũng mở rộng đến các giao diện.

Sự khác biệt ở đây là biểu mẫu thứ hai được coi là bị chặn. Tùy thuộc vào giới hạn, bạn sẽ không thể thêm hoặc xóa các phần tử khỏi bộ sưu tập này.

? extends Tupper bounded wildcard. Trong thực tế, nó mô tả một ràng buộc phân cấp giữa một số loại (?) ở mức thấp, và Book ở mức cao. Đó là bao gồm, vì vậy bạn có thể lưu trữ các trường hợp Book trong đó, nhưng nếu bạn có các lớp khác và giao diện, chẳng hạn như:

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 {} 

... bạn có thể tìm thấy bất kỳ của những trường hợp bên trong một Collection<? extends Book>.

Nhớ lại rằng tôi đã đề cập rằng bạn không thể thêm hoặc xóa nội dung khỏi bộ sưu tập này? Hãy nhớ quy ước này:

Nhà sản xuất mở rộng; siêu tiêu dùng.

Lưu ý rằng đây là quan điểm của bộ sưu tập; nếu bộ sưu tập bị ràng buộc với extends, đó là nhà sản xuất; nếu nó bị ràng buộc với super, đó là một người tiêu dùng.

Điều đó nói rằng, từ quan điểm của bộ sưu tập này, nó đã tạo ra tất cả các bản ghi của nó, vì vậy bạn không thể thêm các bản ghi mới vào nó.

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

Nếu đó là trường hợp mà bạn đã có nó ràng buộc với ? super Book, bạn không thể lấy yếu tố từ nó trong một cách lành mạnh - bạn sẽ phải khôi phục lại theo Object thay vào đó, mà không phải là ngắn gọn.

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 

Chủ yếu, nếu bạn đang bị ràng buộc với ? super T, bạn chỉ bao giờ có ý định chèn yếu tố vào bộ sưu tập đó.

Câu hỏi tiếp theo: BookCase extends ArrayList<Book> có nghĩa là BookCase extends Book?

No. A BookCase trong trường hợp đó là ArrayList, không phải là Book. Nó sẽ xảy ra là trường hợp danh sách mảng bị ràng buộc để lưu trữ sách, nhưng chính nó là không phải a Book.

+0

Vì vậy, đại diện cho trẻ em thấp nhất qua T, trong khi có nghĩa là T và chỉ T - không có con. Điều này có đúng không? – Evorlor

+0

Nó sử dụng cùng mối quan hệ với trẻ em, vì vậy bạn có thể lưu trữ trẻ em bên trong 'T'. Trong ví dụ của tôi, bạn có thể đặt một thể hiện của 'Manga' bên trong một' Bộ sưu tập '. – Makoto

+0

Hãy để tôi thực hiện một số chỉnh sửa ở đây. Có một chút nhầm lẫn tại chỗ. – Makoto

0

List<Book> là danh sách có thể chứa bất kỳ sách nào. Vì không có giới hạn về loại sách, bạn có thể thêm bất kỳ cuốn sách nào vào cuốn sách đó.

List<? extends Book> cũng là danh sách chứa sách nhưng có thể có các hạn chế khác. Có thể trên thực tế nó là một List<PoetryBook> và nó chỉ có thể lưu trữ các trường hợp của PoetryBook, bạn chỉ có thể chắc chắn rằng mọi mục bên trong danh sách này là một cuốn sách. Do hạn chế này, bạn không thể thêm bất kỳ mục nào vào danh sách này.

Xem xét phương thức lấy danh sách sách và trả về sách có nhiều trang nhất.

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

Phương thức này có thể được gọi với List<Book> hoặc với List<PoetryBook>.

Nếu bạn thay đổi chữ ký để

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

sau đó nó cũng có thể được sử dụng với Set<ScienceBook> hoặc bất cứ điều gì đó là iterable và chứa sách.

+0

... nhưng một phương pháp bổ sung một số thơ vào danh sách sách: 'addSomePoetry (List )' sẽ không hoạt động. (chỉ làm tròn giải thích của bạn.) –

+0

Vâng, đó là vấn đề. – phlogratos