Jakie są różnice między List
, List<?>
, List<T>
, List<E>
, i List<Object>
?
1. Lista
List
: jest typem surowym, dlatego nie typesafe
. Wygeneruje błąd czasu wykonywania tylko wtedy, gdy rzutowanie jest złe. Chcemy błędu czasu kompilacji, gdy rzutowanie jest złe. Nie zaleca się używania.
2. Lista <?>
List<?>
jest nieograniczonym znakiem wieloznacznym. Ale nie jestem pewien, do czego to służy? Mogę wydrukować List<?>
bez problemu:
public static void test(List<?> list){
System.out.println(list); // Works
}
Dlaczego nie mogę dodać elementów do List<?>
?
public static void test(List<?> list){
list.add(new Long(2)); // Error
list.add("2"); // Error
System.out.println(list);
}
3. Lista <T>
public static void test(List<T> list){ // T cannot be resolved
System.out.println(list);
}
Nie rozumiem tej składni. Widziałem coś takiego i działa:
public <T> T[] toArray(T[] a){
return a;
}
Czasami widzę <T>
, albo <E>
, lub <U>
, <T,E>
. Czy wszystkie są takie same, czy reprezentują coś innego?
4. Lista <Obiekt>
Daje to błąd „Metoda test(List<Object>)
nie ma zastosowania do argumentu List<String>
”:
public static void test(List<Object> list){
System.out.println(list);
}
Jeśli spróbuję tego, otrzymam komunikat „Nie można przesyłać z List<String>
do List<Object>
”:
test((List<Object>) names);
Jestem zdezorientowany. String
jest podklasą Object
, więc dlaczego nie List<String>
jest podklasą List<Object>
?
2
. Piszę kilka kodów, aby to zademonstrować2
. tyvmList<Object>
?Na koniec: Chociaż String jest podzbiorem Object, ale List <String> nie jest dziedziczony z List <Object>.
źródło
List<Object>
?Notacja
List<?>
oznacza „listę czegoś (ale nie mówię co)”. Ponieważ kodtest
działa dla dowolnego obiektu na liście, działa to jako parametr metody formalnej.Użycie parametru type (jak w punkcie 3) wymaga zadeklarowania parametru type. Składnia Java jest umieszczana
<T>
przed funkcją. Jest to dokładnie analogiczne do zadeklarowania formalnej nazwy parametru metodzie przed użyciem nazw w treści metody.Jeśli chodzi o
List<Object>
nieakceptowanieList<String>
, ma to sens, ponieważString
nie jestObject
; jest to podklasaObject
. Poprawka polega na zadeklarowaniupublic static void test(List<? extends Object> set) ...
. Ale toextends Object
jest zbędne, ponieważ każda klasa rozszerza bezpośrednio lub pośrednioObject
.źródło
List<Object>
?List<?>
ponieważ lista jest jakiegoś konkretnego, ale nieznanego typu.List<Object>
byłby naprawdę „listą czegokolwiek”, ponieważ rzeczywiście może zawierać wszystko.Powodem, dla którego nie można rzutować
List<String>
naList<Object>
to, że to pozwala naruszać ograniczeniamiList<String>
.Pomyśl o następującym scenariuszu: Jeśli mam
List<String>
, powinien on zawierać tylko obiekty typuString
. (Która jestfinal
klasą)Jeśli mogę to rzucić na
List<Object>
, to pozwala mi to dodaćObject
do tej listy, naruszając w ten sposób pierwotną umowęList<String>
.Ogólnie więc, jeśli klasa
C
dziedziczy po klasieP
, nie można powiedzieć, żeGenericType<C>
dziedziczy również poGenericType<P>
.NB: Skomentowałem to już w poprzedniej odpowiedzi, ale chciałem ją rozwinąć.
źródło
List<Object>
?List<Object>
ponieważ w pewnym sensie pokonuje to cel generycznych. Są jednak przypadki, w których stary kod możeList
przyjmować różne typy, dlatego warto zmodyfikować kod, aby użyć parametryzacji typów, aby uniknąć ostrzeżeń kompilatora dla typów surowych. (Ale funkcjonalność pozostaje niezmieniona)Radziłbym czytać łamigłówki Java. W deklaracjach całkiem dobrze wyjaśnia dziedziczenie, generyczne, abstrakcje i symbole wieloznaczne. http://www.javapuzzlers.com/
źródło
Porozmawiajmy o nich w kontekście historii Java;
List
:Lista oznacza, że może zawierać dowolny obiekt. Lista była w wersji wcześniejszej niż Java 5.0; Java 5.0 wprowadziła Listę dla kompatybilności wstecznej.
List<?>
:?
oznacza nieznany Obiekt, a nie żaden Obiekt;?
wprowadzenie symboli wieloznacznych służy rozwiązaniu problemu zbudowanego przez Typ ogólny; zobacz symbole wieloznaczne ; ale powoduje to również inny problem:List< T> List< E>
Oznacza ogólną deklarację przy założeniu braku typu T lub E w projekcie Lib.
List< Object>
oznacza ogólną parametryzację.źródło
W trzecim punkcie „T” nie może zostać rozwiązany, ponieważ nie jest zadeklarowany, zwykle kiedy deklarujesz klasę ogólną, możesz użyć „T” jako nazwy parametru typu powiązanego , wiele przykładów online, w tym
samouczki Oracle,używa „T” jako nazwa parametru type, powiedzmy na przykład, że deklarujesz klasę jak:mówisz, że
FooHandler's
operateOnFoo
metoda oczekuje zmiennej typu „T”, która jest zadeklarowana w samej deklaracji klasy, mając to na uwadze, możesz później dodać inną metodę, taką jakwe wszystkich przypadkach T, E lub U, tam są wszystkie identyfikatory parametru typu, możesz mieć nawet więcej niż jeden parametr typu, który używa składni
w twoim czwartym ponint, chociaż skutecznie Sting jest podtypem Object, w klasach generycznych nie ma takiej relacji,
List<String>
nie jest podtypem,List<Object>
ponieważ są to dwa różne typy z punktu widzenia kompilatora, najlepiej to wyjaśnia ten wpis na bloguźródło
Teoria
String[]
można rzucić naObject[]
ale
List<String>
nie można rzucić naList<Object>
.Ćwiczyć
W przypadku list jest to bardziej subtelne, ponieważ w czasie kompilacji nie jest sprawdzany typ parametru List przekazywany do metody. Definicja metody mogłaby równie dobrze powiedzieć
List<?>
- z punktu widzenia kompilatora jest równoważna. Dlatego w przykładzie nr 2 OP podano błędy wykonania, a nie błędy kompilacji.Jeśli
List<Object>
ostrożnie obchodzisz się z parametrem przekazanym do metody, aby nie wymuszać sprawdzania typu w żadnym elemencie listy, możesz zdefiniować metodę za pomocą,List<Object>
ale w rzeczywistości zaakceptowaćList<String>
parametr z kodu wywołującego.Odp .: Więc ten kod nie spowoduje błędów kompilacji ani błędów w czasie wykonywania i faktycznie (a może zaskakująco?) Zadziała:
B. Ten kod spowoduje błąd w czasie wykonywania:
C. Ten kod spowoduje błąd w czasie wykonywania (
java.lang.ArrayStoreException: java.util.Collections$UnmodifiableRandomAccessList Object[]
):W B parametr
set
nie jest wpisywanyList
w czasie kompilacji: kompilator widzi go jakoList<?>
. Wystąpił błąd środowiska wykonawczego, ponieważ w czasie wykonywaniaset
staje się rzeczywistym obiektem przekazywanym zmain()
, i jest toList<String>
.List<String>
Nie mogą być oddane doList<Object>
.W C, parametr
set
wymaga użyciaObject[]
. Nie ma błędu kompilacji ani błędu czasu wykonywania, gdy jest wywoływany zString[]
obiektem jako parametrem. To dlatego, żeString[]
rzuca naObject[]
. Ale rzeczywisty obiekt otrzymany przeztest()
pozostajeString[]
, nie zmienił się. Zatemparams
obiekt staje się równieżString[]
. A elementu 0String[]
nie można przypisać doLong
!(Mam nadzieję, że mam wszystko tutaj, jeśli moje rozumowanie jest błędne, jestem pewien, że społeczność mi o tym poinformuje. ZAKTUALIZOWANO: Zaktualizowałem kod w przykładzie A, tak aby faktycznie się kompilował, jednocześnie pokazując poczynione punkty.)
źródło
List<Object> cannot be applied to List<String>
. Nie możesz przejśćArrayList<String>
do metody, która się spodziewaArrayList<Object>
.Problem 2 jest OK, ponieważ „System.out.println (set);” oznacza „System.out.println (set.toString ());” set jest instancją List, więc kompilator wywoła List.toString ();
Problem 3: te symbole są takie same, ale możesz nadać im inną specyfikację. Na przykład:
Problem 4: Kolekcja nie pozwala na kowariancję parametrów typu. Ale tablica pozwala na kowariancję.
źródło
Masz rację: String jest podzbiorem Object. Ponieważ String jest bardziej „precyzyjny” niż Object, powinieneś rzucić go, aby użyć go jako argumentu dla System.out.println ().
źródło