Różnica między typem generycznym a typem wieloznacznym

Jestem nowicjuszem w Generic i moje pytanie brzmi: jaka jest różnica między dwoma funkcjami:

Funkcja 1:

public static <E> void funct1  (List<E> list1) {

}

Funkcja 2:

public static void funct2(List<?> list) {

}
Dzięki.
Author: Fio, 2012-06-08

7 answers

Pierwszy podpis mówi: list1 jest listą Es.

Drugi podpis mówi: list jest listą instancji jakiegoś typu, ale nie znamy tego typu.

Różnica staje się oczywista, gdy próbujemy zmienić metodę, więc potrzeba drugiego argumentu, który należy dodać do listy wewnątrz metody:

import java.util.List;

public class Experiment {
    public static <E> void funct1(final List<E> list1, final E something) {
        list1.add(something);
    }

    public static void funct2(final List<?> list, final Object something) {
        list.add(something); // does not compile
    }
}

Pierwszy działa ładnie. I nie można zmienić drugiego argumentu na coś, co faktycznie się skompiluje.

Właśnie znalazłem jeszcze ładniejszy demonstracja różnicy:

public class Experiment {
    public static <E> void funct1(final List<E> list) {
        list.add(list.get(0));
    }

    public static void funct2(final List<?> list) {
        list.add(list.get(0)); // !!!!!!!!!!!!!! won't compile !!!!!!!!!
    }
}

Może być po co nam <?>, skoro ogranicza to tylko to ,co możemy z nim zrobić (tak jak zrobił to @Babu_Reddy_H w komentarzach). Widzę następujące zalety wersji wildcard:

  • Rozmówca musi wiedzieć mniej o obiekcie, który przechodzi. Na przykład, jeśli mam mapę List: Map<String, List<?>> mogę przekazać jej wartości do funkcji bez określania typu elementów listy. Więc

  • Jeśli rozdam przedmioty tak sparametryzowany aktywnie ograniczam to, co ludzie wiedzą o tych obiektach i co mogą z nimi zrobić (o ile trzymają się z dala od niebezpiecznego rzucania).

Te dwa mają sens, gdy je łączę: List<? extends T>. Na przykład rozważ metodę List<T> merge(List<? extends T>, List<? extends T>), która łączy dwie listy wejściowe z nową listą wyników. Pewnie, że możesz wprowadzić jeszcze dwa parametry typu, ale po co? To byłby koniec precyzowania rzeczy.

  • w końcu wildcards mogą mieć niższe granice, więc dzięki listom możesz sprawić, że metoda add zadziała, podczas gdy get nie daje Ci niczego użytecznego. Oczywiście rodzi to kolejne pytanie: dlaczego leki generyczne nie mają niższych granic?

Aby uzyskać bardziej szczegółową odpowiedź zobacz: kiedy stosować metody generyczne, a kiedy stosować wild-card? i http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ203

 32
Author: Jens Schauder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:34

Generyki sprawiają, że zbiór jest bardziej bezpieczny.

List<E> : E tutaj jest parametr Type, który może być użyty do określenia typu zawartości listy, ale był sposób No, aby sprawdzić, jaka była zawartość podczas runtime.

Generics are checked only during compilation time.

<? extends String> : zostało to specjalnie wbudowane w Javę, aby poradzić sobie z problemem, który był z parametrem Type. "? extends String" oznacza, że lista ta może mieć

objects which IS-A String.

Dla NP:

Klasa zwierząt Dog class extends Animal Klasa tygrysa extends Animal

Więc użycie "public void go(ArrayList<Animal> a)" będzie NOT accept psem lub Tygrysem jako jego treścią, ale zwierzęciem.

"public void go(ArrayList<? extends Animal> a)" jest co jest potrzebne, aby ArrayList take in Dog and Tiger type.

Sprawdź, czy nie ma odniesień w Head First Java.

 6
Author: Kumar Vivek Mitra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-08 05:03:54

Pierwsza jest funkcją, która przyjmuje parametr, który musi być listą pozycji typu E.

Drugi przykładowy Typ nie jest zdefiniowany

List<?> list

Więc możesz przekazać listę dowolnego typu obiektów.

 1
Author: Pramod Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-08 05:21:32

Lista jako typ parametru mówi, że parametr musi być listą elementów z dowolnym typem obiektu. Co więcej, można powiązać parametr E, aby zadeklarować odniesienia do elementów listy wewnątrz ciała funkcji.

Lista jako typ parametru ma taką samą semantykę, z tą różnicą, że nie ma innego sposobu deklarowania odniesień do pozycji na liście niż użycie Object. Inne posty dają dodatkowe subtelne różnice.

 1
Author: Gene,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-07 13:48:58

Zwykle wyjaśniam różnicę między E > a ? > przez porównanie z kwantyfikacjami logicznymi, czyli kwantyfikacją uniwersalną i kwantyfikacją egzystencjalną.

  • odpowiada "forall E,..."
  • odpowiada "istnieje coś (oznaczonego ) takiego, że ...."

Dlatego następująca deklaracja metody ogólnej oznacza, że dla wszystkich typów klasy E definiujemy funct1

public static <E> void funct1  (List<E>; list1) {

}

Następujący rodzajnik deklaracja metody oznacza, że dla jakiejś istniejącej klasy oznaczonej przez ?>, definiujemy funct2.

public static void funct2(List<?> list) {

}
 1
Author: user3509406,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-16 10:54:36

(od czasu edycji) te dwie sygnatury funkcji mają taki sam efekt w kodzie zewnętrznym - obie przyjmują dowolny List jako argument. Symbol wieloznaczny jest odpowiednikiem parametru typ, który jest używany tylko raz.

 0
Author: newacct,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-08 07:28:38

Oprócz wyżej wymienionych różnic, istnieje również dodatkowa różnica: możesz jawnie ustawić argumenty typu dla wywołania metody generycznej:

List<Apple> apples = ...
ClassName.<Banana>funct2(apples); // for some reason the compiler seems to be ok
                               // with type parameters, even though the method has none

ClassName.<Banana>funct1(apples); // compiler error: incompatible types: List<Apple>
                                  //                 cannot be converted to List<Banana>

(ClassName jest nazwą klasy zawierającej metody.)

 0
Author: fabian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-24 10:32:08