Wie wandelt man eine Liste von Supertypen in eine Liste von Subtypen um?

Lesezeit: 1 Minute

Wie wandelt man eine Liste von Supertypen in eine Liste
Jeremy Logan

Angenommen, Sie haben zwei Klassen:

public class TestA {}
public class TestB extends TestA{}

Ich habe eine Methode, die a zurückgibt List<TestA> und ich möchte alle Objekte in dieser Liste umwandeln TestB so dass ich am Ende mit a List<TestB>.

1646257029 678 Wie wandelt man eine Liste von Supertypen in eine Liste
neues Konto

Einfach umwerfen List<TestB> funktioniert fast; aber es funktioniert nicht, weil Sie einen generischen Typ eines Parameters nicht in einen anderen umwandeln können. Sie können jedoch einen zwischengeschalteten Wildcard-Typ umwandeln, und dies ist zulässig (da Sie zu und von Wildcard-Typen umwandeln können, nur mit einer ungeprüften Warnung):

List<TestB> variable = (List<TestB>)(List<?>) collectionOfListA;

  • Bei allem Respekt, ich denke, das ist eine Art Hacky-Lösung – Sie umgehen die Typsicherheit, die Java Ihnen zu bieten versucht. Schauen Sie sich zumindest dieses Java-Tutorial an (docs.oracle.com/javase/tutorial/java/generics/subtyping.html) und überlegen Sie, warum Sie dieses Problem haben, bevor Sie es auf diese Weise beheben.

    – jfritz42

    30. November 2012 um 19:37 Uhr


  • @ jfritz42 Ich würde es nicht als “Ausweichen” -Sicherheit bezeichnen. In diesem Fall haben Sie Wissen, das Java nicht hat. Dafür ist Casting da.

    – Planky

    11. Juli 2014 um 17:08 Uhr

  • Damit kann ich schreiben: List myList = (List)(List)(new ArrayList()); Dies würde zur Laufzeit abstürzen. Ich bevorzuge etwas wie List myList = new ArrayList();

    – Bentaye

    12. Oktober 2017 um 11:56 Uhr


  • Ich stimme @jfritz42 ehrlich zu. Ich hatte das gleiche Problem und habe mir die von ihm bereitgestellte URL angesehen und konnte mein Problem ohne das Casting lösen.

    – Sam Orozco

    4. September 2018 um 22:27 Uhr

  • @ jfritz42 Dies unterscheidet sich funktional nicht vom Umwandeln eines Objekts in eine Ganzzahl, die vom Compiler zugelassen wird

    – kcazllerraf

    3. Oktober 2019 um 18:43 Uhr

1646257030 546 Wie wandelt man eine Liste von Supertypen in eine Liste
Salandur

Das Casting von Generika ist nicht möglich, aber wenn Sie die Liste auf andere Weise definieren, ist es möglich, sie zu speichern TestB drin:

List<? extends TestA> myList = new ArrayList<TestA>();

Sie müssen immer noch die Typprüfung durchführen, wenn Sie die Objekte in der Liste verwenden.

  • Dies ist nicht einmal gültige Java-Syntax.

    – neues Konto

    3. Dezember 2013 um 19:57 Uhr

  • Das stimmt, aber es war eher illustrativ als sachlich richtig

    – Salandur

    3. Dezember 2013 um 21:13 Uhr

  • Ich habe die folgende Methode: createSingleString(List-Objekte) Innerhalb dieser Methode rufe ich String.valueOf(Object) auf, um die Liste in eine Zeichenfolge zu reduzieren. Es funktioniert hervorragend, wenn die Eingabe List,List usw. ist. Danke!

    – Chris Sprague

    10. Juni 2014 um 16:32 Uhr


  • Was ist, wenn TestA eine Schnittstelle ist?

    – Suvitruf – Andrej Apanasik

    12. Februar 2015 um 11:19 Uhr

  • Können Sie einen MCVE geben, wo dies tatsächlich funktioniert? Ich bekomme Cannot instantiate the type ArrayList<? extends TestA>.

    – Nateowami

    7. April 2015 um 12:37 Uhr

1646257031 777 Wie wandelt man eine Liste von Supertypen in eine Liste
Steve Kuo

Du kannst wirklich nicht*:

Beispiel entnommen dieses Java-Tutorial

Angenommen, es gibt zwei Arten A und B so dass B extends A. Dann ist folgender Code richtig:

    B b = new B();
    A a = b;

Der vorherige Code ist gültig, weil B ist eine Unterklasse von A. Was passiert nun mit List<A> und List<B>?

Es stellt sich heraus, dass List<B> ist keine Unterklasse von List<A> deshalb wir kann nicht schreiben

    List<B> b = new ArrayList<>();
    List<A> a = b; // error, List<B> is not of type List<A>

Außerdem wir kippen sogar schreiben

    List<B> b = new ArrayList<>();
    List<A> a = (List<A>)b; // error, List<B> is not of type List<A>

*: Um das Casting zu ermöglichen, brauchen wir einen gemeinsamen Elternteil für beide List<A> und List<B>: List<?> zum Beispiel. Die folgende ist gültig:

    List<B> b = new ArrayList<>();
    List<?> t = (List<B>)b;
    List<A> a = (List<A>)t;

Sie erhalten jedoch eine Warnung. Sie können es durch Hinzufügen unterdrücken @SuppressWarnings("unchecked") zu deiner Methode.

  • Außerdem kann die Verwendung des Materials in diesem Link die ungeprüfte Konvertierung vermeiden, da der Compiler die gesamte Konvertierung entschlüsseln kann.

    –Ryan H

    11. Januar 2013 um 15:49 Uhr

Mit Java 8 können Sie das tatsächlich

List<TestB> variable = collectionOfListA
    .stream()
    .map(e -> (TestB) e)
    .collect(Collectors.toList());

Ich denke jedoch, dass Sie in die falsche Richtung werfen … wenn die Methode eine Liste von zurückgibt TestA Objekte, dann ist es wirklich nicht sicher, sie dorthin zu werfen TestB.

Grundsätzlich bitten Sie den Compiler, Sie ausführen zu lassen TestB Operationen auf einem Typ TestA das unterstützt sie nicht.

1646257032 593 Wie wandelt man eine Liste von Supertypen in eine Liste
Marco13

Da dies eine weit verbreitete Frage ist und die aktuellen Antworten hauptsächlich erklären, warum es nicht funktioniert (oder hackige, gefährliche Lösungen vorschlagen, die ich niemals im Produktionscode sehen möchte), halte ich es für angebracht, eine weitere Antwort hinzuzufügen die Fallstricke und eine mögliche Lösung.


Warum dies generell nicht funktioniert, wurde bereits in anderen Antworten aufgezeigt: Ob die Konvertierung tatsächlich gültig ist oder nicht, hängt von den Typen der Objekte ab, die in der ursprünglichen Liste enthalten sind. Wenn Objekte in der Liste vorhanden sind, deren Typ nicht vom Typ ist TestBaber von einer anderen Unterklasse von TestAdann ist die Besetzung nicht gültig.


Natürlich die Besetzung dürfen gültig sein. Manchmal haben Sie Informationen zu den Typen, die für den Compiler nicht verfügbar sind. In diesen Fällen ist es möglich, die Listen zu werfen, aber im Allgemeinen ist es möglich nicht empfohlen:

Man könnte entweder …

  • … die ganze Liste werfen oder
  • … caste alle Elemente der Liste

Die Implikationen des ersten Ansatzes (der der derzeit akzeptierten Antwort entspricht) sind subtil. Es scheint auf den ersten Blick richtig zu funktionieren. Aber wenn es falsche Typen in der Eingabeliste gibt, dann a ClassCastException wird geworfen, vielleicht an einer ganz anderen Stelle im Code, und es kann schwierig sein, dies zu debuggen und herauszufinden, wo das falsche Element in die Liste gerutscht ist. Das schlimmste Problem ist, dass jemand sogar die ungültigen Elemente hinzufügen könnte nach dem Die Liste wurde gecastet, was das Debuggen noch schwieriger macht.

Das Problem des Debuggens dieser falschen ClassCastExceptions kann mit gelindert werden Collections#checkedCollection Familie von Methoden.


Filtern der Liste basierend auf dem Typ

Eine typsicherere Methode zum Konvertieren von a List<Supertype> zu einem List<Subtype> ist eigentlich zu Filter die Liste, und erstellen Sie eine neue Liste, die nur Elemente enthält, die einen bestimmten Typ haben. Für die Durchführung eines solchen Verfahrens gibt es einige Freiheitsgrade (z. B. hinsichtlich der Behandlung von null Einträge), aber eine mögliche Implementierung könnte so aussehen:

/**
 * Filter the given list, and create a new list that only contains
 * the elements that are (subtypes) of the class c
 * 
 * @param listA The input list
 * @param c The class to filter for
 * @return The filtered list
 */
private static <T> List<T> filter(List<?> listA, Class<T> c)
{
    List<T> listB = new ArrayList<T>();
    for (Object a : listA)
    {
        if (c.isInstance(a))
        {
            listB.add(c.cast(a));
        }
    }
    return listB;
}

Diese Methode kann verwendet werden, um beliebige Listen zu filtern (nicht nur mit einer bestimmten Subtyp-Supertyp-Beziehung bezüglich der Typparameter), wie in diesem Beispiel:

// A list of type "List<Number>" that actually 
// contains Integer, Double and Float values
List<Number> mixedNumbers = 
    new ArrayList<Number>(Arrays.asList(12, 3.4, 5.6f, 78));

// Filter the list, and create a list that contains
// only the Integer values:
List<Integer> integers = filter(mixedNumbers, Integer.class);

System.out.println(integers); // Prints [12, 78]

1646257033 642 Wie wandelt man eine Liste von Supertypen in eine Liste
Martinazeit

Sie können nicht werfen List<TestB> zu List<TestA> wie Steve Kuo erwähnt, ABER Sie können den Inhalt von ausgeben List<TestA> hinein List<TestB>. Versuche Folgendes:

List<TestA> result = new List<TestA>();
List<TestB> data = new List<TestB>();
result.addAll(data);

Ich habe diesen Code nicht ausprobiert, daher gibt es wahrscheinlich Fehler, aber die Idee ist, dass er das Datenobjekt durchlaufen und die Elemente (TestB-Objekte) zur Liste hinzufügen sollte. Ich hoffe, das funktioniert für Sie.

  • warum wurde das runtergestimmt? Es war nützlich für mich, und ich sehe keine Erklärung für die Ablehnung.

    – Zod

    28. März 2011 um 13:56 Uhr

  • Dieser Beitrag ist sicherlich nicht falsch. Aber die Person, die die Frage gestellt hat, braucht schließlich eine Liste von TestB. In diesem Fall ist diese Antwort irreführend. Sie können alle Elemente in List zu List hinzufügen, indem Sie List.addAll(List) aufrufen. Aber Sie können List.addAll(List) nicht verwenden;

    – prageth

    17. August 2012 um 11:27 Uhr

916370cookie-checkWie wandelt man eine Liste von Supertypen in eine Liste von Subtypen um?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy