2014-09-11 18 views
5

Czytam poprzez następnym rozdziale w samouczku Java: http://docs.oracle.com/javase/tutorial/java/generics/capture.htmlJava Generics Wildcard vs użytkowania Wpisywane Generics

Zaczyna się od stwierdzenia, że ​​poniższy kod generuje błąd ze względu na fakt, że przechwytywanie nie mogą być zamieniane na Object więc metoda set nie może potwierdzić, że obiekt jest typu capture # 1:

import java.util.List; 

public class WildcardError { 

    void foo(List<?> i) { 
     i.set(0, i.get(0)); 
    } 
} 

I nieco zrozumieć uzasadnienie tego. i.get zwraca obiekt, a kompilator nie może ustalić, czy obiekt jest typu przechwytywania nr 1, więc nie może dopasować go do drugiego argumentu w sposób bezpieczny dla określonych typów.

Następnie zaleca stosowanie poniższy kod, aby to metoda pracy:

public class WildcardFixed { 

    void foo(List<?> i) { 
     fooHelper(i); 
    } 


    // Helper method created so that the wildcard can be captured 
    // through type inference. 
    private <T> void fooHelper(List<T> l) { 
     l.set(0, l.get(0)); 
    } 

} 

I nieco zrozumieć dlaczego ten kod działa, jak również, w tym kodzie, l.get jest gwarancją typu T, tak może on być przekazany jako argument typu T.

Co ja nie rozumiem, dlaczego nie można po prostu użyć metody takie jak to, bez pomocnika:

class GenericsTest { 
    static <K> void bar(List<K> l) { 
     l.set(0, l.get(l.size() - 1)); 
    } 

    public static void main(String[] args) { 
     List<Integer> lst = Arrays.asList(1, 2, 3, 4); 
     bar(lst); 
     System.out.println(lst); // [4, 3, 2, 4] 
    } 
} 

tj. Jeśli zamierzasz używać wnioskowania o typie, dlaczego nie używać po prostu generycznych wyrażeń pisanych nad wieloznacznikiem i funkcją pomocnika? Czy w tym scenariuszu jest jakaś korzyść z używania symboli wieloznacznych? W jakich przypadkach wolisz używać wieloznacznika zamiast typowego?

+0

Co oznacza "static void", według ciebie? –

+0

@Mike Dla mnie oznacza to, że K jest parametrem typu, który ma zastosowanie tylko do zakresu metody. Java korzysta z inferencji typów, aby dowiedzieć się, co K ma być w czasie wykonywania. W pokazanym przykładzie przechodzę w instancji listy , więc kompilator wskazuje, że K ma wartość całkowitą. K może być dowolnym typem referencji, a typy odniesienia rozciągają się od Object, więc uważam K, że w tym przypadku jest to po prostu inny sposób pisania . – Shashank

+0

@Mike Nie wiem ... wydaje się być taki sam jak zwykła metoda void. Nie wiedziałem, że K miał coś wspólnego z powrotem. Używam tylko następującej składni: http://docs.oracle.com/javase/tutorial/java/generics/methods.html – Shashank

Odpowiedz

1

Pierwsza uwaga, że ​​void foo(List<?> i) i void <K> foo(List<K> i) zarówno przyjąć dokładnie ten sam zestaw argumentów - zakładając, że nie jawnie określić K na generycznego przypadku metody, żadnego argumentu, który może być przekazany do jednego podpisu funkcji mogą być przekazywane do inne i odwrotnie. Zatem do "kodu zewnętrznego" oba podpisy są równie przydatne.

Biorąc pod uwagę, że są one równoważne, ten, który ma mniej parametrów typu jest prostszy i zawsze powinien być preferowany. Podczas prezentowania publicznego interfejsu API do kodu zewnętrznego, zawsze powinieneś go prezentować w najprostszej formie, a jedynie minimalną ilość rzeczy niezbędnych do jej zabezpieczenia. Typ List<?> wystarczy, aby wyrazić, że może on przyjmować dowolny typ List, więc tego właśnie powinniśmy użyć.

Używamy wewnętrznie parametru typu K, aby rozwiązać niektóre problemy generyczne, ale jest to po prostu niefortunny wewnętrzny szczegół implementacji, którego kodu zewnętrznego nie trzeba obchodzić. Dlatego powinniśmy ukryć tę brzydotę i zawinąć ją w ładniejszy podpis funkcji podczas tworzenia publicznego API.

Oprócz celów abstrakcyjnych, kolejnym powodem, dla którego możesz chcieć określonego podpisu, jest nadpisanie metody nadklasy z tym podpisem. Podczas nadpisywania nie można dodawać parametrów typu.

0

Myślę, że przykład, o którym wspominasz w samouczku, odnosi się do przypadków obsługi, gdy musisz zaakceptować parametry z niezwiązanymi znakami wieloznacznymi <?>. Może tak być w przypadku interakcji ze starszym kodem lub rozszerzenia istniejącej klasy o taki typ wieloznaczny.

Głównie będziesz używać rzeczy takich jak <T extends U> lub <? extends T>, gdy potrzebujesz elastyczności, ale nie chcesz rezygnować z sprawdzania typu.