2015-04-24 9 views
19

Jestem nowy w Javie. W this dokumentu dają to jako przypadek użycia za korzystanie wieloznaczny:W Javie, co może zrobić dzika karta, którą zwykli generics nie mogą zrobić?

static void printCollection(Collection c) { 
    Iterator i = c.iterator(); 
    for (int k = 0; k < c.size(); k++) { 
     System.out.println(i.next()); 
    } 
} 

To jest ich rozwiązanie:

static void printCollection(Collection<?> c) { 
    for (Object e : c) { 
     System.out.println(e); 
    } 
} 

Ale co mogłem zrobić to samo bez dzikich kart:

static <T> void printCollection(Collection<T> c) { 
    Iterator i = c.iterator(); 
    for (int k = 0; k < c.size(); k++) { 
     System.out.println(i.next()); 
    } 
} 

Czy ktoś może mi pokazać prosty przypadek użycia, w którym zwykłe leki generyczne nie będą działały, ale będzie dzika karta?

Aktualizacja: Odpowiedzi tutaj When to use wildcards in Java Generics? NIE mówią nam o konieczności użycia symboli wieloznacznych. W rzeczywistości jest odwrotnie.

+0

Nie jestem do końca pewny, ale uważam, że ostatnia wersja bez dzikiej karty może odradzać wiele wersji tej samej metody, specyficznych dla każdego typu "Collection" po wywołaniu metody z różnymi typami kolekcji. Ten z dziką kartą poradzi sobie z nimi wszystkimi. – tiguchi

+3

@NobuGames W Javie będzie to jedna metoda, ponieważ generics są wymazywane w czasie kompilacji. – ILMTitan

+0

[oficjalne dokumenty] (https://docs.oracle.com/javase/tutorial/java/generics/wildcardGuidelines.html) zapewniają dobrą odpowiedź na to pytanie. – Droidman

Odpowiedz

4

Jedno dzikie karty pozwalają nam zrobić, to zadeklarować typów, które są niezależne od kierunku określonego parametru typu, na przykład „listy jakiejkolwiek liście” :

List<List<?>> listOfAnyList = ...; 

listOfAnyList.add(new ArrayList<String>()); 
listOfAnyList.add(new ArrayList<Double>()); 

jest to niemożliwe bez zamiennika : * ponieważ listy elementów mogą mieć różne typy od siebie.

A jeśli staramy się go capture, okaże się, że nie możemy:

static <E> void m(List<List<E>> listOfParticularList) {} 

m(listOfAnyList); // <- this won't compile 

Inną rzeczą dzikie karty pozwalają nam to zrobić parametrów typu nie jest ustawiony dolny pułap. (Parametr typu może być uznana z extends związany, ale nie super związany. **)

class Protector { 
    private String secretMessage = "abc"; 

    void pass(Consumer<? super String> consumer) { 
     consumer.accept(secretMessage); 
    } 
} 

Załóżmy pass został uznany zamiast wziąć Consumer<String>. Załóżmy teraz, że mieliśmy Consumer<Object>:

class CollectorOfAnything implements Consumer<Object> { 
    private List<Object> myCollection = new ArrayList<>(); 

    @Override 
    public void accept(Object anything) { 
     myCollection.add(anything); 
    } 
} 

Problem polega na tym, że nie możemy przekazać ją do metody przyjmującej Consumer<String>. Zgłoszenie Consumer<? super String> oznacza, że ​​możemy przekazać każdego konsumenta, który akceptuje numer String. (Zobacz także Java Generics: What is PECS?.)

W większości przypadków symbole wieloznaczne pozwalają nam składać czyste deklaracje.

Jeśli nie musimy używać typu, nie musimy zadeklarować dla niego parametru typu.


* Technicznie również możliwe z raw type, ale surowe typy są zniechęceni.

** Nie wiem, dlaczego Java nie zezwala na super dla parametru typu. 4.5.1. Type Arguments of Parameterized Types może wskazywać, że to ma coś wspólnego z ograniczeniem typu wnioskowania:

przeciwieństwie do zwykłych zmiennych typu zadeklarowanych w podpis metody, żaden typ wnioskowanie jest wymagane w przypadku korzystania z wieloznacznych. W związku z tym dopuszczalne jest deklarowanie niższych granic na karcie wieloznacznej [& hellip;].

+0

Teraz, jeśli tylko Ty możesz również zawierać przydatne praktyczne zastosowanie symboli wieloznacznych ... ponieważ nigdy ich nie znalazłem – Mehrdad

+0

@Mehrdad 'Mapa , Coś>' A jak to jest? super' nie przydatne? Jest używany wszędzie. – Radiodef

+0

Sposób, w jaki go widzę, 'Class ' jest konieczny tylko dlatego, że surowe typy są odradzane, nie dlatego, że wildcard dodaje jakąkolwiek wartość. Jeśli chodzi o '? super' ... Nigdy nie czułem potrzeby korzystania z tego, ale może to tylko ja; gdzie widziałeś, że to konieczne? – Mehrdad

0

T oznacza ogólny rodzaj tej struktury danych. W twoim ostatnim przykładzie nie używasz go, a NIE jest to rzeczywisty typ (na przykład String), a ponieważ go nie używasz, nie ma to w tym przypadku znaczenia.

Na przykład, jeśli masz kolekcję i próbujesz przekazać ją do metody, która akceptuje kolekcję, działa, ponieważ nie ma typu T w ścieżce klas, więc jest uważana za zmienną. Jeśli próbowałeś przekazać tę samą kolekcję do metody, która akceptuje kolekcję, to nie działałoby, ponieważ masz łańcuch String w swojej klasie, więc nie jest zmienną.

0

Jako przykład podać Listę.

  • List<?> może być klasą nadrzędną List<A>.

na przykład

List<B> bList = new ArrayList<>(); // B is a class defined in advance 
List<?> list = bList; 

nigdy nie można używać <T> w tej sytuacji.

  • <?> ma przechwytywania symboli wieloznacznych.

tutaj,

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

powyższy kod nie może zostać skompilowany. Można go naprawić:

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

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

zobaczyć więcej, http://docs.oracle.com/javase/tutorial/java/generics/capture.html

mogę myśleć tylko o dwóch Obecnie później może aktualizować.