2011-01-14 29 views
9

Mam na myśli "Generics in Java Programming Language" Gilada Brachy i nie mam pojęcia o stylu deklaracji. Poniższy kod znajduje się na stronie 8:Czy ktoś może wyjaśnić deklarację tych ogólnych metod java?

interface Collection<E> 
{ 
    public boolean containsAll(Collection<?> c); 
    public boolean addAll(Collection<? extends E> c); 
} 


interface Collection<E> 
{ 
    public <T> boolean containsAll(Collection<T> c); 
    public <T extends E> boolean addAll(Collection<T> c); 
    // hey, type variables can have bounds too! 
} 

Mój punkt widzenia wynika z drugiej deklaracji. To nie jest dla mnie jasne, jaki jest cel deklaracja <T> służy w następującej linii:

public <T> boolean containsAll(Collection<T> c); 

Metoda ma już typ (logiczna) z nim związane.

Po co używać <T> i co mówi komplementowi?

Myślę, że moje pytanie musi być nieco bardziej szczegółowe.

Dlaczego piszesz:

public <T> boolean containsAll(Collection<T> c); 

vs

public boolean containsAll(Collection<T> c); 

To nie jest dla mnie jasne, jaki jest cel <T> jest w pierwszej deklaracji containsAll.

+1

Jeśli rozumiesz PECS, zrozumiesz to. Sprawdź tutaj http://stackoverflow.com/questions/4535930/regarding-pecs-java-generics –

+1

W Oracle/Sun JDK 6 pierwszy przykład jest używany, więc nie wiem, czy używałbyś drugiego. –

+1

I dlaczego jest lepszy niż 'public boolean containsAll (Collection c);'? –

Odpowiedz

3

O ile mogę powiedzieć, w tym przypadku <T> nie dostarcza niczego użytecznego w ogóle.Tworzy ona metodę, która jest całkowicie funkcjonalnie równoważna z tymi, które używają symbolu wieloznacznego.

Oto kilka przykładów, gdzie byłoby być użyteczne:

public List<?> transform(List<?> in); 
//vs 
public <T> List<T> transform(List<T> in); 

W powyższym przykładzie, można skorelować typ zwracany z typem wejściowego. Pierwszy przykład nie może skorelować typu środowiska wykonawczego dwóch symboli wieloznacznych.

public void add(List<?> list, Object obj); 
//vs 
public <T> void add(List<? super T> list, T obj); 

W powyższym pierwsza metoda nie będzie nawet w stanie dodać obj do list ponieważ nie może być uważana za bezpieczną typu. Ogólny parametr w drugim gwarantuje, że list może pomieścić dowolny typ obj.

3

Metoda ma już skojarzony typ (boolean).

To jest typ zwrotu w postaci. Pełny typ metody to "metoda, która pobiera parametr Collection<T> (dla niektórych) i zwraca wartość boolean".

I tu właśnie pojawia się T: parametr funkcji używa go. Innymi słowy, ta metoda może być wywołana z różnymi typami jako argument. Jedynym ograniczeniem tego typu jest to, że muszą one implementować interfejs Collection<T>, który sam w sobie opiera się na ogólnym argumencie T (typ obiektów przechowywanych w kolekcji).

+0

Ale dlaczego jest to lepsze od pozornie funkcjonalnego odpowiednika 'public boolean containsAll (Collection c)'? Podobnie dlaczego nie "public boolean addAll (Collection c)"? –

+0

Jest to lepsze, ponieważ treść lub definicja metody może odnosić się do typu T, a kod może uniknąć rzutowania i być gwarantowany przez kompilator w sposób bezpieczny dla typu. Zobacz moją odpowiedź na dalsze wyjaśnienia. –

+0

@Dave Costa: Nie widzę odpowiedzi od ciebie, ale to, czy użycie '' pomaga ** innym ** metodom jest nieistotne. Mówimy o ** tych ** metodach, które nie przynoszą żadnych korzyści. –

0

Spróbuj skompilować go bez <T>.

Zasadniczo mówi kompilatorowi, że ta metoda zawiera rodzajowy. Nie jest to wymagane w pierwszym przykładzie, ponieważ? jest przypadkiem specjalnym, a druga metoda odwołuje się do typu zdefiniowanego w samym interfejsie.

W przypadku niepowiązanej notatki publiczny interfejs nie jest wymagany. Metody interfejsu są domyślnie publiczne, więc możesz zaoszczędzić trochę pisania.

0

Deklaruje rodzaj ogólny T używany przez metodę. Podczas gdy ogólny typ E jest taki sam dla całego interfejsu T jest ograniczony do metody, dla której jest zadeklarowany.

2

? jest po prostu symbolem wieloznacznym. Oznacza to, że metoda zaakceptuje kolekcję dowolnego typu.

Parametr typu dla metody jest <T>. Zasadniczo przypisuje ona symbolowi wieloznacznemu nazwę, którą można następnie odnieść w dowolnym miejscu w deklaracji i definicji metody.

lepszego zobrazowania różnicy byłoby, gdyby zwracany typ metody były zróżnicowane w zależności od typu, który został przekazany w.

Załóżmy, że zaczęło się od sposobu jak

Object getRandomElement(Collection<?> c)

This zaakceptuje każdą kolekcję, ale nie ma możliwości ograniczenia jej typu zwrotu. Tak więc osoba dzwoniąca musiała odesłać wynik do dowolnego typu, jakiego oczekiwał - co powinno zadziałać, ale podnosi niebezpieczne ostrzeżenia o konwersji typu.

z parametrem typu byś zamiast pisać

<T> T getRandomElement(Collection<T> c)

W tym przypadku, jeśli wywołanie tej metody z Collection<String>, kompilator wie, że powróci do String.

+0

Dobrze powiedziane. Myślę, że dobra zasada jest taka, że ​​parametr type jest wymagany tylko wtedy, gdy masz sparametryzowany typ zwracania (np. 'Collections.emptyList() ') lub gdy trzeba skorelować typ zwracany lub inny parametr z parametrem parametryzowanym lub ogólnym. –

Powiązane problemy