Najskuteczniejszy sposób rzucania Listy Do Listy

Mam List<SubClass>, które chcę traktować jako List<BaseClass>. Wygląda na to, że nie powinno to być problemem, ponieważ rzut SubClass na {[4] } jest prosty, ale mój kompilator narzeka, że rzut jest niemożliwy.

Więc, jaki jest najlepszy sposób, aby uzyskać odniesienie do tych samych obiektów, co List<BaseClass>?

W tej chwili robię nową listę i kopiuję starą:

List<BaseClass> convertedList = new ArrayList<BaseClass>(listOfSubClass)

Ale jak rozumiem, to musi stworzyć zupełnie nową listę. Chciałbym nawiązać do oryginalnej listy, jeśli możliwe!

Author: Riley Lark, 2011-02-22

9 answers

Składnia tego rodzaju przypisania używa wieloznaczności:

List<SubClass> subs = ...;
List<? extends BaseClass> bases = subs;

Ważne jest, aby zdać sobie sprawę, że List<SubClass>jest Nie wymienne z List<BaseClass>. Kod, który zachowuje odniesienie do List<SubClass>, oczekuje, że każdy element na liście będzie SubClass. Jeśli inna część kodu odnosi się do listy jako List<BaseClass>, kompilator nie będzie narzekał, gdy dodano BaseClass lub AnotherSubClass. Ale to spowoduje ClassCastException dla pierwszego fragmentu kodu, który zakłada, że wszystko na liście jest SubClass.

Zbiory generyczne nie zachowują się tak samo jak tablice w Javie. Tablice są kowariantne, czyli można to zrobić:

SubClass[] subs = ...;
BaseClass[] bases = subs;

Jest to dozwolone, ponieważ tablica " zna " Typ swoich elementów. Jeśli ktoś spróbuje zapisać coś, co nie jest instancją SubClass w tablicy (poprzez odniesienie bases), zostanie wyrzucony wyjątek runtime.

Zbiory generyczne Nie Nie "znają" ich typ składowy; informacja ta jest "kasowana" przy kompilacji czas. W związku z tym nie mogą wywołać wyjątku runtime, gdy wystąpi nieprawidłowy sklep. Zamiast tego ClassCastException zostanie podniesione w odległym, trudnym do powiązania punkcie w kodzie, gdy wartość zostanie odczytana z kolekcji. Jeśli uwzględnisz ostrzeżenia kompilatora dotyczące bezpieczeństwa typów, unikniesz tych błędów w czasie wykonywania.

 139
Author: erickson,
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
2011-02-22 18:20:13

Erickson już wyjaśnił, dlaczego nie można tego zrobić, ale tutaj kilka rozwiązań:

Jeśli chcesz usunąć tylko elementy z listy bazowej, w zasadzie twoja metoda otrzymująca powinna być zadeklarowana jako przyjmująca List<? extends BaseClass>.

Ale jeśli tak nie jest i nie możesz tego zmienić, możesz zawinąć listę za pomocą Collections.unmodifiableList(...), co pozwala zwrócić listę supertype parametru argumentu. (Pozwala to uniknąć problemu typesafety, rzucając nieobsługiwaną Operacjęexception przy próbach wstawiania.)

 30
Author: Paŭlo Ebermann,
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
2011-02-22 21:05:01

Jak wyjaśnił @ erickson, jeśli naprawdę chcesz mieć odniesienie do oryginalnej listy, upewnij się, że żaden kod nie wstawi niczego do tej listy, jeśli kiedykolwiek chcesz użyć go ponownie pod jej oryginalną deklaracją. Najprostszym sposobem, aby go zdobyć, jest po prostu wrzucenie go do zwykłej, starej, niegenerycznej listy: {]}

List<BaseClass> baseList = (List)new ArrayList<SubClass>();

Nie polecam tego, jeśli nie wiesz, co dzieje się z listą i sugerujesz zmianę kodu potrzebnego do zaakceptowania listy, którą posiadasz.

 10
Author: hd42,
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
2011-02-22 18:45:35

List<BaseClass> convertedList = Collections.checkedList(listOfSubClass, BaseClass.class)

 1
Author: jtahlborn,
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
2011-02-22 18:46:23

To, co próbujesz zrobić, jest bardzo przydatne i uważam, że muszę to robić bardzo często w kodzie, który piszę. Przykładowy przypadek użycia:

Powiedzmy, że mamy Interfejs Foo i mamy zorking pakiet, który ma ZorkingFooManager, który tworzy i zarządza instancjami package-private ZorkingFoo implements Foo. (Bardzo powszechny scenariusz.)

Więc ZorkingFooManager musi zawierać private Collection<ZorkingFoo> zorkingFoos, ale musi ujawniać public Collection<Foo> getAllFoos().

Większość programistów Javy Nie zastanowiłaby się dwa razy przed zaimplementowaniem getAllFoos() jako przydzielania nowy ArrayList<Foo>, wypełniając go wszystkimi elementami z zorkingFoos i zwracając go. Cieszy mnie myśl, że około 30% wszystkich cykli zegara zużywanych przez kod Javy działający na milionach maszyn na całej planecie nie robi nic poza tworzeniem takich bezużytecznych kopii tablic, które są zbieranymi śmieciami mikrosekundami po ich utworzeniu.

Rozwiązaniem tego problemu jest oczywiście obniżenie zbioru. Oto najlepszy sposób, aby to zrobić:

static <T,U extends T> List<T> downCastList( List<U> list )
{
    return castList( list );
}

Co przynosi us do funkcji castList():

static <T,E> List<T> castList( List<E> list )
{
    @SuppressWarnings( "unchecked" )
    List<T> result = (List<T>)list;
    return result;
}

Zmienna pośrednia result jest konieczna ze względu na wypaczenie języka java:

  • return (List<T>)list; generuje wyjątek "unchecked cast" ; jak dotąd wszystko jest w porządku; ale wtedy:

  • @SuppressWarnings( "unchecked" ) return (List<T>)list; jest nielegalnym użyciem adnotacji tłumić-warnings.

Tak więc, nawet jeśli nie jest koszerne użycie @SuppressWarnings na return instrukcji, widocznie dobrze jest użyć jej na przypisaniu, więc dodatkowa zmienna" result" rozwiązuje ten problem. (Powinien być zoptymalizowany zarówno przez kompilator, jak i przez JIT.)

 1
Author: Mike Nakis,
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-04-06 18:30:20

Coś takiego też powinno działać:

public static <T> List<T> convertListWithExtendableClasses(
    final List< ? extends T> originalList,
    final Class<T> clazz )
{
    final List<T> newList = new ArrayList<>();
    for ( final T item : originalList )
    {
        newList.add( item );
    }// for
    return newList;
}
Nie wiem, dlaczego clazz jest potrzebny w Eclipse..
 0
Author: olivervbk,
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-03-25 23:06:12

Co powiesz na rzucanie wszystkich elementów? Utworzy nową listę, ale odwoła się do oryginalnych obiektów ze starej listy.

List<BaseClass> convertedList = listOfSubClass.map(x -> (BaseClass)x).collect(Collectors.toList());
 0
Author: drordk,
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-05-27 15:49:46

Jest to kompletny roboczy fragment kodu wykorzystujący Generyki, do przerzucania listy podklas do superklasy.

Metoda wywołująca przekazująca Typ podklasy

List<SubClass> subClassParam = new ArrayList<>();    
getElementDefinitionStatuses(subClassParam);

Metoda Callee, która akceptuje dowolny Podtyp klasy bazowej

private static List<String> getElementDefinitionStatuses(List<? extends 
    BaseClass> baseClassVariableName) {
     return allElementStatuses;
    }
}
 0
Author: kanaparthikiran,
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-09-05 20:15:25

Poniżej znajduje się przydatny fragment, który działa. Tworzy nową listę tablic, ale tworzenie obiektów JVM nad głową jest istotne.

Widziałem, że inne odpowiedzi są niekoniecznie skomplikowane.

List<BaseClass> baselist = new ArrayList<>(sublist);
 -1
Author: sigirisetti,
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-07-19 14:23:07