2015-02-11 28 views
6

Po próbie zrozumienia pojęć pod numerem Spring MVC natknąłem się na wyrażenie Collection<? extends Book>, którego nigdy wcześniej nie widziałem. Próbowałem go rozwiązać samodzielnie, ale nie widzę różnicy między używaniem Collection<? extends Book> i Collection<Book>. Zgadywałem, że dopuszcza to tylko rozszerzenia Książki, ale także pozwala na Księgę. Zarysuj to. Próbowałem używać Google, ale od kiedy? jest symbolem wieloznacznym w google, dzięki czemu wyszukiwanie jest prawie niemożliwe. Mam szukane stackoverflow dla odpowiedzi, ale wszystkie pytania na ten temat (takie jak List<? extends MyType> i <? extends > Java syntax) już zakładają znajomość Collection<? extends T>. Oto kod, który początkowo intryguje moje zainteresowanie:Kolekcja <? extends T> vs Kolekcja <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); 
    } 
} 

Co <? extends T> zrobić? Czym się różni od <T>?

EDIT:

Followup pytanie: Czy BookCase extends ArrayList<Book> oznacza, że ​​BookCase rozciąga Book?

+2

"Kolekcja " to "Kolekcja"? rozszerza Book> 'ale nie' Collection '. – immibis

+0

Takie czasy, żałuję, że nie mogę zaznaczyć zielonymi znacznikami wielu odpowiedzi. W szczególności odpowiedzi Makoto i Adama. Chociaż wszystkie odpowiedzi były pomocne, w szczególności pomogły mi to zrozumieć. Jeśli więc szukasz odpowiedzi na to pytanie, sugeruję, abyś przeczytał oba, przynajmniej w tym komentarze. Dziękuję wszystkim!! – Evorlor

Odpowiedz

3

Rozważmy następujący

class Animal { } 
class Horse extends Animal { } 

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

Bez rozszerza składnię można użyć tylko dokładną klasę w podpisie

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

Wraz z zamiennika rozciąga można dopuszczać żadnych podklasy Animal

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

Generalnie lepiej jest używać? rozszerza składnię, ponieważ sprawia, że ​​Twój kod jest bardziej przydatny do ponownego wykorzystania i przyszłościowy.

+1

Ale koń jest zwierzęciem, a konkretny() przyjmuje listę zwierząt ... więc dlaczego lista koni nie zostanie zaakceptowana? – Evorlor

+0

Generics nie działa w taki sam sposób, jak standardowe sygnatury metod, yes foo (Animal animal) może przyjmować dowolny typ Animal. – Adam

+1

@Evorlor, ponieważ jeśli określisz parametr type jako jakiś obiekt, kompilator chce, aby był to dokładnie ten obiekt –

2

Oto przykład, który jasno pokazuje różnicę między Collection<? extends Book> i 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); 
    } 
} 

że kod kompiluje. Jeśli zmienisz parametr konstruktora s na BookCase na Collection<Book>, ten przykład nie zostanie skompilowany.

4

Sprawdź w dokumentacji dokumentację this.

W ogóle, jeśli Foo jest podtypem (podklasa lub podinterfejsu) Baru, a G jest jakiś typ rodzajowy oświadczenie, że nie jest to przypadek, że G jest podtypem G. Jest to prawdopodobnie najtrudniejsza rzecz, trzeba się uczyć o rodzajach ogólnych, ponieważ jest to sprzeczne z naszą głęboko zakorzenioną intuicją.

Zobacz także next page about wildcards.

Zasadniczo po napisaniu void doStuff(List<Book>){} można tylko wyświetlać rzeczy tylko na liście obiektów Book.

NieNovel s, nieMagazine s, nieComicBook s.

Ponownie, to dlatego, że chociaż Novel rozciąga Book, G<Novel>faktycznie nie rozciągająG<Book>. Nie jest to bardzo intuicyjne, ale przykład w dokumentacji pomoże ci to zobaczyć.

3

Zarówno Collection<Book> i Collection<? extends Book> reprezentuje kolekcję, która może pomieścić Book instancje i obiektów, które mogą być uznane za Book poprzez oznacza związek. Ta właściwość rozciąga się również na interfejsy.

Różnica polega na tym, że ta ostatnia forma jest uznawana za ograniczona. W zależności od powiązania nie można dodawać ani usuwać elementów z tej kolekcji.

? extends T to upper bounded wildcard. W efekcie opisuje hierarchiczne powiązanie między pewnym typem (?) na najniższym końcu i Book na wyższym końcu. Jest włącznie, dzięki czemu można przechowywać wystąpienia Book tam, ale jeśli miał inne klasy i interfejsy, takie jak:

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

... można znaleźć żadnego z tych przypadków wewnątrz Collection<? extends Book>.

Przypomnij sobie, że wspomniałem, że możesz nie móc dodawać ani usuwać rzeczy z tej kolekcji? Zapamiętaj tę konwencję:

Producent rozciąga się; konsument super.

Należy zauważyć, że jest to z punktu widzenia kolekcji; jeśli kolekcja jest ograniczona extends, to jest producentem; jeśli jest ograniczony super, jest konsumentem.

To powiedziało, z tej kolekcji, że już wyprodukował wszystkie swoje zapisy, więc nie można dodawać do niego nowych.

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

Gdyby było tak, że trzeba było to związane z ? super Book, nie można odzyskać elementy z niego w sposób rozsądny - trzeba by je odzyskać jak Object zamiast, który nie jest zwięzły.

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 

Głównie, jeśli są związane z ? super T, tylko kiedykolwiek zamierza wkładką elementy w tej kolekcji.

Pytanie uzupełniające: czy BookCase extends ArrayList<Book> oznacza, że ​​BookCase extends Book?

Nr A BookCase w tym przypadku jest ArrayList, nie Book. Tak się składa, że ​​lista tablic jest zobowiązana do przechowywania książek, ale sama w sobie jest , a nie a Book.

+0

Tak więc reprezentuje najniższe dziecko do T, natomiast oznacza T, a tylko T - brak dzieci. Czy to jest poprawne? – Evorlor

+0

Używa tej samej relacji z dziećmi, więc możesz przechowywać dzieci wewnątrz 'T'. W moim przykładzie możesz umieścić instancję 'Manga' wewnątrz' Collection '. – Makoto

+0

Pozwól mi dokonać pewnych poprawek tutaj. Trochę zamieszania w miejscu. – Makoto

0

List<Book> to lista, która może zawierać dowolną książkę. Ponieważ nie ma ograniczeń co do rodzaju książki, możesz dodać do niej dowolną książkę.

List<? extends Book> jest również listą zawierającą książki, ale mogą obowiązywać dalsze ograniczenia. Może w rzeczywistości jest to List<PoetryBook> i może przechowywać tylko instancje PoetryBook, możesz być tylko pewien, że każdy element na tej liście jest książką. Z powodu tego możliwego ograniczenia nie możesz dodawać żadnych elementów do tej listy.

Rozważ metodę, która pobiera listę książek i zwraca książkę z największą liczbą stron.

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

Ten sposób może być nazywany z List<Book> lub List<PoetryBook>.

Jeżeli zmienisz podpis

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

to może być również używany z Set<ScienceBook> lub czegokolwiek, co jest iterable i zawiera książki.

+0

... ale metoda, która dodaje kilka poezji do listy książek: 'addSomePoetry (List )' nie działa. (zaokrąglenie twojego wyjaśnienia). –

+0

Tak, o to właśnie chodzi. – phlogratos