Java Generics (symbole wieloznaczne)


109

Mam kilka pytań dotyczących ogólnych symboli wieloznacznych w Javie:

  1. Jaka jest różnica między List<? extends T>i List<? super T>?

  2. Co to jest ograniczony symbol wieloznaczny, a co nieograniczony symbol wieloznaczny?


W przypadku, gdy odpowiedź Teda Gao została usunięta (ponieważ zawierała tylko łącze), oto post na blogu, do którego prowadzi link.
royhowie

Odpowiedzi:


123

W swoim pierwszym pytaniu, <? extends T>a <? super T>przykłady ograniczonych symbole wieloznaczne. Nieograniczony symbol wieloznaczny wygląda <?>i zasadniczo oznacza <? extends Object>. To luźno oznacza, że ​​rodzaj ogólny może być dowolnego typu. Ograniczony symbol wieloznaczny ( <? extends T>lub <? super T>) nakłada ograniczenie na typ, mówiąc, że albo musi rozszerzyć określony typ ( <? extends T>jest to górna granica), albo musi być przodkiem określonego typu ( <? super T>jest znane jako dolna granica) .

Samouczki języka Java zawierają całkiem dobre wyjaśnienia dotyczące typów ogólnych w artykułach Symbole wieloznaczne i Więcej zabawy z symbolami wieloznacznymi .


Żeby zrobić to dobrze, jeśli A <B i B <C to: <A rozszerza C> jest błędne?
Pablo Fernandez

Jeśli przez A <B masz na myśli, że A rozszerza B, to A rozszerza C. Nie użyłbyś tego w składni symboli wieloznacznych, powiedziałbyś <? rozszerza C>, aby ograniczyć twój wybór do A lub B.
Bill the Lizard

iw takim przypadku jeśli powiem <? super C> jaka byłaby różnica?
Pablo Fernandez

3
Chciałem tylko polecić inną referencję na temat Java Generics: angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
Zach Scrivena

3
@Pablo: <? super C>oznaczałoby to, że twój typ jest ograniczony do czegoś powyżej Cw hierarchii typów. (Przepraszamy za bardzo późną odpowiedź. Wydaje mi się, że nie otrzymaliśmy powiadomień o komentarzach 2 lata temu?)
Bill the Lizard

49

Jeśli masz hierarchię klas A, B jest podklasą A, a C i D obie są podklasą B, jak poniżej

class A {}
class B extends A {}
class C extends B {}
class D extends B {}

Następnie

List<? extends A> la;
la = new ArrayList<B>();
la = new ArrayList<C>();
la = new ArrayList<D>();

List<? super B> lb;
lb = new ArrayList<A>(); //fine
lb = new ArrayList<C>(); //will not compile

public void someMethod(List<? extends B> lb) {
    B b = lb.get(0); // is fine
    lb.add(new C()); //will not compile as we do not know the type of the list, only that it is bounded above by B
}

public void otherMethod(List<? super B> lb) {
    B b = lb.get(0); // will not compile as we do not know whether the list is of type B, it may be a List<A> and only contain instances of A
    lb.add(new B()); // is fine, as we know that it will be a super type of A 
}

Ograniczony symbol wieloznaczny jest taki, jak ? extends Btam, gdzie B jest jakimś typem. Oznacza to, że typ jest nieznany, ale można na nim umieścić „wiązanie”. W tym przypadku jest ograniczona przez jakąś klasę, która jest podklasą B.


Zakładam, że List<? super B>opisuje jako List akceptuje typ, który jest klasą nadrzędną klasy B ? Dlaczego C i D nie kompilują się hmm?
Volkan Güven

38

Josh Bloch ma również dobre wyjaśnienie, kiedy używać, superoraz extendsw tym wykładzie wideo google io, w którym wspomina o mnemoniku Producer extendsConsumersuper .

Ze slajdów prezentacji:

Załóżmy, że chcesz dodać metody zbiorcze do Stack<E>

void pushAll(Collection<? extends E> src);

- src jest producentem E.

void popAll(Collection<? super E> dst);

- dst jest konsumentem E.


Przeczytałem książkę Blocha, ale nadal nie widzę różnicy między extends i super w tym konkretnym przypadku.
Pablo Fernandez

Obejrzyj wideo, myślę, że jest całkiem jasne. Myślę też, że powinieneś zadać kolejne pytanie na temat tego „Jaka jest różnica między List <? Extends T> a List <? Super T>”, gdzie, miejmy nadzieję, uzyskasz więcej odpowiedzi. (jeśli tak, dodaj link z tego miejsca)
puste

2
Łóżka - dlaczego nie uwzględnić przykładu w tej odpowiedzi, aby była kompletna i samodzielna. Linki są ulotne.
James Schek

3

Może się zdarzyć, że będziesz chciał ograniczyć rodzaje typów, które mogą być przekazywane do parametru typu. Na przykład metoda, która operuje na liczbach, może chcieć akceptować tylko wystąpienia Number lub jej podklasy. Do tego służą parametry typu ograniczonego.

Collection<? extends MyObject> 

oznacza, że ​​może akceptować wszystkie obiekty, które są w relacji IS- A z MyObject (tj. każdy obiekt będący typem myObject lub możemy powiedzieć dowolny obiekt dowolnej podklasy MyObject) lub obiekt klasy MyObject.

Na przykład:

class MyObject {}

class YourObject extends MyObject{}

class OurObject extends MyObject{}

Następnie,

Collection<? extends MyObject> myObject; 

zaakceptuje tylko MyObject lub elementy podrzędne MyObject (tj. każdy obiekt typu OurObject lub YourObject lub MyObject, ale nie będzie to żaden obiekt z nadklasy MyObject).


1

Ogólnie,

Jeśli struktura zawiera elementy o typie formularza ? extends E, możemy wydobyć elementy ze struktury, ale nie możemy wstawić elementów do struktury

List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14); // compile-time error
assert ints.toString().equals("[1, 2, 3.14]"); 

Aby umieścić elementy w strukturze, potrzebujemy innego rodzaju symbolu wieloznacznego o nazwie Wildcards with super,

 List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
    List<Integer> ints = Arrays.asList(5, 6);
    Collections.copy(objs, ints);
    assert objs.toString().equals("[5, 6, four]");

    public static <T> void copy(List<? super T> dst, List<? extends T> src) {
          for (int i = 0; i < src.size(); i++) {
                dst.set(i, src.get(i));
         }
    }

1

Generyczne symbole wieloznaczne są tworzone, aby umożliwić wielokrotne użycie metod działających w kolekcji.

Na przykład, jeśli metoda ma parametr List<A>, możemy dać tylko List<A>tej metodzie. W pewnych okolicznościach jest to strata dla funkcji tej metody :

  1. Jeśli ta metoda czyta tylko obiekty z List<A>, to powinniśmy mieć możliwość oddania List<A-sub>tej metodzie. (Ponieważ A-sub JEST A)
  2. Jeśli ta metoda wstawia tylko obiekty do List<A>, to powinniśmy mieć możliwość oddania List<A-super>tej metodzie. (Ponieważ A JEST A-super)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.