Mam listę, która jest zadeklarowana w ten sposób:
List<? extends Number> foo3 = new ArrayList<Integer>();
Próbowałem dodać 3 do foo3. Jednak pojawia się następujący komunikat o błędzie:
The method add(capture#1-of ? extends Number) in the type List<capture#1-of ?
extends Number> is not applicable for the arguments (ExtendsNumber)
List<? extends Number>
nie oznacza to „listy obiektów różnych typów, z których wszystkie się rozszerzająNumber
”. Oznacza „listę obiektów jednego typu, które się rozszerzająNumber
”.Odpowiedzi:
Przepraszam, ale nie możesz.
Deklaracja wieloznaczna
List<? extends Number> foo3
oznacza, że zmiennafoo3
może zawierać dowolną wartość z rodziny typów (zamiast dowolnej wartości określonego typu). Oznacza to, że którekolwiek z tych zadań są prawnymi:Biorąc pod uwagę to, jaki typ obiektu możesz dodać do
List foo3
tego, byłby legalny po którymkolwiek z powyższych możliwychArrayList
przypisań:Integer
ponieważfoo3
może wskazywać naList<Double>
.Double
ponieważfoo3
może wskazywać naList<Integer>
.Number
ponieważfoo3
może wskazywać naList<Integer>
.Nie możesz dodać żadnego obiektu,
List<? extends T>
ponieważ nie możesz zagwarantować, na jaki rodzaj obiektuList
wskazuje, więc nie możesz zagwarantować, że obiekt jest w tym dozwolonyList
. Jedyną „gwarancją” jest to, że możesz tylko z niego czytać, a otrzymaszT
podklasę lubT
.Logika odwrotna dotyczy
super
npList<? super T>
. Są to legalne:Nie możesz odczytać konkretnego typu T (np.
Number
),List<? super T>
Ponieważ nie możesz zagwarantować, na jaki typList
naprawdę wskazuje. Jedyną „gwarancją”, jaką masz, jest możliwość dodania wartości typuT
(lub dowolnej podklasyT
) bez naruszenia integralności wskazywanej listy.Doskonałym tego przykładem jest podpis
Collections.copy()
:Zwróć uwagę, w jaki sposób
src
deklaracja listyextends
pozwala mi przekazać dowolną Listę z rodziny pokrewnych typów List i nadal gwarantuje, że wygeneruje wartości typu T lub podklasy T. Ale nie możesz dodawać dosrc
listy.Do
dest
zastosowania deklaracji listasuper
pozwoli mi przekazać żadnej liście z rodziny pokrewnych typów listy i nadal gwarantować mogę napisać wartość określonego typu T do tej listy. Ale nie można zagwarantować, że odczytam wartości określonego typu T, jeśli przeczytam z listy.Więc teraz, dzięki uniwersalnym symbolom generycznym, mogę wykonać dowolne z tych wywołań za pomocą tej jednej metody:
Rozważ ten zagmatwany i bardzo szeroki kod, aby ćwiczyć mózg. Skomentowane wiersze są nielegalne, a powód jest podany po prawej stronie wiersza (należy przewinąć, aby zobaczyć niektóre z nich):
źródło
src
List
zastosowania argumentówextends
czytać z listy src, natomiast gdydest
List
używa argumentówsuper
, aby napisać do dest listy. Pozwala to na jedną metodę, którą można kopiować zList<Integer>
lubList<Double>
doList<Number>
lubList<Object>
.or subclass of T
jest poprawne. Na przykład nie mogę dodać klasyObject
(nadklasyNumber
) do,List<? super Number> foo3
ponieważfoo3
mogły zostać przypisane jako:List<? super Number> foo3 = new ArrayList<Number>
(które mogą zawierać tylkoNumber
lub podklasyNumber
).<? super Number>
odnosi się do typówList<>
s, do których można przypisać, afoo3
nie do rodzajów rzeczy, które można z niego dodać / odczytać. Rodzaje rzeczy, które można dodać / usunąć,foo3
muszą być rzeczami, które można dodać / usunąć z dowolnego rodzaju, doList<>
którego można przypisaćfoo3
.Nie możesz (bez niebezpiecznych rzutów). Możesz tylko z nich czytać.
Problem polega na tym, że nie wiesz, czego dokładnie jest lista. Może to być lista dowolnej podklasy klasy Number, więc kiedy próbujesz wstawić do niej element, nie wiesz, że element faktycznie pasuje do listy.
Na przykład Lista może być listą
Byte
s, więc umieszczenie w niej litery a byłoby błędemFloat
.źródło
„List '<'? Extends Number> jest w rzeczywistości górnym ograniczeniem wieloznacznym!
Symbol wieloznaczny z górną granicą mówi, że każda klasa, która rozszerza samą liczbę lub liczbę, może być użyta jako formalny typ parametru: Problem wynika z faktu, że Java nie wie, jaki typ naprawdę jest List . Musi to być DOKŁADNY i WYJĄTKOWY typ. Mam nadzieję, że to pomoże :)
źródło
To było dla mnie mylące, mimo że czytałem tutaj odpowiedzi, dopóki nie znalazłem komentarza Pawła Minaeva:
Po tym byłem w stanie zrozumieć niesamowite wyjaśnienie BertF. Lista <? rozszerza Number> oznacza? może być dowolnego typu rozszerzającego Number (Integer, Double, itp.) i nie jest jasne w deklaracji (List <? extends Number> list), który z nich to jest, więc kiedy chcesz użyć metody add, nie wiadomo, czy wejście jest tego samego typu lub nie; jaki to w ogóle jest typ?
Więc elementy List <? rozszerza Number> można było ustawić tylko podczas konstruowania.
Zwróć też uwagę na to: kiedy używamy szablonów, mówimy kompilatorowi, z jakim typem się bawimy. T na przykład trzyma ten typ dla nas, ale nie ? robi to samo
Muszę powiedzieć ... To jeden z brudnych do wyjaśnienia / nauczenia
źródło
Zamiast tego możesz to zrobić:
źródło
Możesz go sfałszować, tworząc odwołanie do Listy z innym typem.
(To są „niebezpieczne odlewy” wspomniane przez sepp2k.)
Ponieważ
untypedList
,superclassedList
itrulyclassedList
to tylko odniesienia dolist
, nadal będzie dodawanie elementów do pierwotnego ArrayList.W rzeczywistości nie musisz używać tego
(List<?>)
w powyższym przykładzie, ale możesz potrzebować go w swoim kodzie, w zależności od typu,list
który został Ci dany.Zauważ, że użycie
?
spowoduje ostrzeżenie kompilatora, dopóki nie umieścisz tego nad funkcją:źródło
gdzie rozszerz listę z 'Object', możesz użyć list.add, a kiedy chcesz użyć list.get, wystarczy rzucić Object na swój obiekt;
źródło