So wandeln Sie List <Object> in List <MyClass> um


Antworten:


156

Sie können jedes Objekt jederzeit in einen beliebigen Typ umwandeln, indem Sie es zuerst in Object umwandeln. in deinem Fall:

(List<Customer>)(Object)list; 

Sie müssen sicher sein, dass die Liste zur Laufzeit nur Kundenobjekte enthält.

Kritiker sagen, dass ein solches Casting darauf hinweist, dass etwas mit Ihrem Code nicht stimmt. Sie sollten in der Lage sein, Ihre Typdeklarationen zu optimieren, um dies zu vermeiden. Aber Java-Generika sind zu kompliziert und nicht perfekt. Manchmal weiß man einfach nicht, ob es eine hübsche Lösung gibt, um den Compiler zufrieden zu stellen, obwohl man die Laufzeitarten sehr gut kennt und weiß, was man versucht, ist sicher. In diesem Fall führen Sie einfach das Rohabgießen nach Bedarf durch, damit Sie die Arbeit für zu Hause verlassen können.


8
Das braucht auch @SuppressWarnings("unchecked"). Beachten Sie, dass Sie auch zu (List)statt zu to senden können (Object).
200_erfolg

36

Das liegt daran, dass ein Kunde zwar ein Objekt ist, eine Kundenliste jedoch keine Objektliste. Wenn ja, können Sie jedes Objekt in eine Kundenliste aufnehmen.


2
Nein, ist es nicht. Sie würden die Typensicherheit umgehen, wenn dies zulässig wäre. Beachten Sie, dass Casting nicht bedeutet, eine neue Liste zu erstellen und über die Elemente zu kopieren. Dies bedeutet, dass die einzelne Instanz als ein anderer Typ behandelt wird und Sie daher eine Liste haben, die potenziell Nicht-Kundenobjekte mit einer Typensicherheitsgarantie enthält, die dies nicht sollte. Dies hat nichts mit Java als solchem ​​zu tun. Da Sie der Meinung sind, dass diese Funktion Java im dunklen Zeitalter stecken lässt, fordere ich Sie auf, zu erklären, wie dies Ihrer Meinung nach funktionieren sollte.
Lasse V. Karlsen

1
@ BrainSlugs83 für Ihre Bedarfsliste (Liste <Kunde>) (Objekt);
Muthu Ganapathy Nathan

@ LasseV.Karlsen Ich spreche nicht vom Casting, ich spreche vom Konvertieren. Es würde die Typensicherheit nicht umgehen, es würde sie durchsetzen. Die Implementierung von Generika durch Java, das Fehlen von Operatorüberlastungen, Erweiterungsmethoden und vielen anderen modernen Annehmlichkeiten hat mich episch enttäuscht. - In der Zwischenzeit verfügt .NET über zwei separate Erweiterungsmethoden - eine aufgerufene .Cast<T>()und eine aufgerufene .OfType<T>(). Ersteres führt eine Umwandlung für jedes Element durch (wobei die gewünschten Ausnahmen ausgelöst werden), während letzteres Elemente herausfiltert, die nicht umgewandelt werden können (Sie würden also je nach Verwendungsszenario eines auswählen).
BrainSlugs83

1
@EAGER_STUDENT Ich würde es nicht hinter Java setzen, dass das tatsächlich funktionieren könnte (all dieses lächerliche Löschgeschäft, ich müsste es versuchen ...) - aber nein, ich würde niemals solchen Code schreiben - du verlierst Typensicherheit, was passiert, wenn ein Element in der Sammlung kein instanceofKunde ist?
BrainSlugs83

1
@ BrainSlugs83 Die Person, die die Frage speziell zum Casting gestellt hat. Und wenn Java nicht bereits über die relevanten Methoden verfügt, wie die .NET-Methoden, auf die Sie verweisen (die übrigens immer noch nicht konvertiert werden), sollten Sie sie problemlos hinzufügen können. Dies alles ist jedoch etwas orthogonal zu der vorliegenden Frage, die nach einer bestimmten Syntax fragte.
Lasse V. Karlsen

35

Abhängig von Ihrem anderen Code kann die beste Antwort variieren. Versuchen:

List<? extends Object> list = getList();
return (List<Customer>) list;

oder

List list = getList();
return (List<Customer>) list;

Denken Sie jedoch daran, dass es nicht empfohlen wird, solche ungeprüften Würfe auszuführen.


3
-1 - Dies ist eine wirklich schlechte Angewohnheit, und wenn Sie nicht die Annotation "Unchecked" verwenden, wird sich der Compiler immer noch darüber beschweren
kdgregory

24

Mit Java 8 Streams :

Manchmal ist Brute-Force-Casting in Ordnung:

List<MyClass> mythings = (List<MyClass>) (Object) objects

Aber hier ist eine vielseitigere Lösung:

List<Object> objects = Arrays.asList("String1", "String2");

List<String> strings = objects.stream()
                       .map(element->(String) element)
                       .collect(Collectors.toList());

Es gibt eine Menge Vorteile, aber einer ist, dass Sie Ihre Liste eleganter gestalten können, wenn Sie nicht sicher sind, was sie enthält:

objects.stream()
    .filter(element->element instanceof String)
    .map(element->(String)element)
    .collect(Collectors.toList());

1
Das ist jedoch eher eine Kopie als eine Besetzung.
200_Erfolg

Das im akzeptierten Anser erwähnte Casting hat bei mir nicht funktioniert. Ich war auch auf Java 7. Aber Guava hat FluentIterablefür mich gearbeitet.
Sridhar Sarnobat

Dies ist, wonach ich gesucht habe, ich kannte die Syntax einfach nicht
Fueled By Coffee

23

Sie können eine Doppelbesetzung verwenden.

return (List<Customer>) (List) getList();

8

Beachten Sie, dass ich kein Java-Programmierer bin, aber in .NET und C # wird diese Funktion als Kontravarianz oder Kovarianz bezeichnet. Ich habe mich noch nicht mit diesen Dingen befasst, da sie neu in .NET 4.0 sind, das ich nicht verwende, da es sich nur um Beta handelt. Daher weiß ich nicht, welcher der beiden Begriffe Ihr Problem beschreibt, aber lassen Sie mich das beschreiben technisches Problem damit.

Nehmen wir an, Sie durften gießen. Beachten Sie, ich sage Besetzung , da Sie das gesagt haben, aber es gibt zwei mögliche Operationen, Besetzung und Konvertierung .

Das Konvertieren würde bedeuten, dass Sie ein neues Listenobjekt erhalten, aber Sie sagen Casting, was bedeutet, dass Sie ein Objekt vorübergehend als einen anderen Typ behandeln möchten.

Hier ist das Problem damit.

Was würde passieren, wenn Folgendes zulässig wäre (ich gehe davon aus, dass die Liste der Objekte vor der Besetzung tatsächlich nur Kundenobjekte enthält, andernfalls würde die Besetzung selbst in dieser hypothetischen Version von Java nicht funktionieren):

List<Object> list = getList();
List<Customer> customers = (List<Customer>)list;
list.Insert(0, new someOtherObjectNotACustomer());
Customer c = customers[0];

In diesem Fall würde dies versuchen, ein Objekt, das kein Kunde ist, als Kunden zu behandeln, und Sie würden an einem Punkt einen Laufzeitfehler erhalten, entweder in der Liste oder in der Zuweisung.

Generika sollen Ihnen jedoch typsichere Datentypen wie Sammlungen geben, und da sie das Wort "garantiert" gerne herumwerfen, ist diese Art der Besetzung mit den folgenden Problemen nicht zulässig.

In .NET 4.0 (ich weiß, Ihre Frage betraf Java) ist dies in einigen sehr speziellen Fällen zulässig , in denen der Compiler garantieren kann, dass die von Ihnen ausgeführten Vorgänge sicher sind, im Allgemeinen jedoch nicht zugelassen werden. Gleiches gilt für Java, obwohl ich mir nicht sicher bin, ob ich Pläne zur Einführung von Co- und Kontravarianz in die Java-Sprache habe.

Hoffentlich kann Ihnen jemand mit besseren Java-Kenntnissen als ich die Einzelheiten für die Java-Zukunft oder -Implementierung mitteilen.


3
Die Zukunft von Java ist ... Scala. Im Ernst, der Typ, der Generika auf Java verschraubt hat, hat eine neue Sprache entwickelt, die Java vage ähnlich ist, aber mit Typen wirklich, wirklich vertraut ist: Eine sehr gründliche und konsistente Implementierung der Typbehandlung. Ich denke, niemand weiß mit Sicherheit, welche Scala-Funktionen wann in Java zurückfließen werden.
Carl Smotricz

Eine ausgezeichnete Erklärung der Kovarianz, die die Frage der OP wirklich beantwortet. Gut gemacht.
Kevin Day

Carl: Ich dachte, einige Java-Entwickler haben C # erstellt? :) Wie auch immer, Java wird höchstwahrscheinlich in Zukunft in Richtung Scala gehen, anstatt beispielsweise etwas weniger stark typisiertes.
Esko

@Carl - In Scala gibt es einen subtilen Unterschied darin, dass Listen standardmäßig unveränderlich sind. So im Allgemeinen yo haben das Problem nicht ein Objekt in eine Liste der Kunden , des Hinzufügens, da , wenn Sie dies tun Sie bekommen neue Liste der Objekte .
Brian Agnew

Äh ... technisch gesehen ist dies korrekt - aber noch vor .NET 4.0 - Sie können dies mit regulären IEnumerable-Erweiterungsmethoden (.Cast <> und .OfType <>) tun - also müssen Sie nicht das tiefe Ende verlassen, wenn Sie möchten nur eine starke Typiteration.
BrainSlugs83

7

Ein anderer Ansatz wäre die Verwendung eines Java 8-Streams.

    List<Customer> customer = myObjects.stream()
                                  .filter(Customer.class::isInstance)
                                  .map(Customer.class::cast)
                                  .collect(toList());

1
Vielen Dank, diese Lösung ist so schön, mit Methodenreferenz
thang

1
Ich hätte nie gedacht, dass jemand so weit nach unten scrollen wird: D
d0x

3

Sie sollten einfach die Liste durchlaufen und alle Objekte einzeln umwandeln


3

Sie können so etwas tun

List<Customer> cusList = new ArrayList<Customer>();

for(Object o: list){        
    cusList.add((Customer)o);        
}

return cusList; 

Oder die Java 8-Methode

list.stream().forEach(x->cusList.add((Customer)x))

return cuslist;

2

Sie können nicht, weil List<Object>und List<Customer>sind nicht im selben Vererbungsbaum.

Sie können Ihrer List<Customer>Klasse einen neuen Konstruktor hinzufügen , der a benötigt, List<Object>und dann die Liste durchlaufen, die jeweils Objectin a umgewandelt Customerund Ihrer Sammlung hinzugefügt wird. Beachten Sie, dass eine ungültige Besetzungsausnahme auftreten kann, wenn der Anrufer List<Object>etwas enthält, das keine ist Customer.

Bei generischen Listen geht es darum, sie auf bestimmte Typen zu beschränken. Sie versuchen, eine Liste zu erstellen, die alles enthalten kann (Bestellungen, Produkte usw.), und sie in eine Liste zusammenzufassen, die nur Kunden aufnehmen kann.


2

Sie können eine neue Liste erstellen und die Elemente hinzufügen:

Beispielsweise:

List<A> a = getListOfA();
List<Object> newList = new ArrayList<>();
newList.addAll(a);

1

Am besten erstellen Sie ein neues Element List<Customer>, durchlaufen es List<Object>, fügen jedes Element zur neuen Liste hinzu und geben es zurück.


1

Wie andere bereits betont haben, können Sie sie nicht sicher wirken, da a List<Object>kein a ist List<Customer>. Sie können eine Ansicht in der Liste definieren, in der die Typprüfung direkt durchgeführt wird. Verwenden von Google-Sammlungen wäre:

return Lists.transform(list, new Function<Object, Customer>() {
  public Customer apply(Object from) {
    if (from instanceof Customer) {
      return (Customer)from;
    }
    return null; // or throw an exception, or do something else that makes sense.
  }
});

1

Ähnlich wie bei Bozho oben. Mit dieser Methode können Sie hier eine Problemumgehung durchführen (obwohl es mir selbst nicht gefällt):

public <T> List<T> convert(List list, T t){
    return list;
}

Ja. Es wird Ihre Liste in Ihren gewünschten generischen Typ umwandeln.

Im obigen Fall können Sie folgenden Code ausführen:

    List<Object> list = getList();
    return convert(list, new Customer());

Ich mag diese Lösung. Selbst wenn Sie SuppressWarnings hinzufügen müssen, ist es besser, es an einer Stelle hinzuzufügen als in jedem unsicheren Casting.
Robinson

1

Je nachdem, was Sie mit der Liste tun möchten, müssen Sie sie möglicherweise nicht einmal in eine Liste umwandeln List<Customer>. Wenn Sie Customerder Liste nur Objekte hinzufügen möchten , können Sie dies wie folgt deklarieren:

...
List<Object> list = getList();
return (List<? super Customer>) list;

Dies ist legal (nicht nur legal, sondern auch korrekt) - die Liste ist von "einem Supertyp für den Kunden"), und wenn Sie sie an eine Methode übergeben, die lediglich Objekte zur Liste hinzufügt, dann die oben genannten Generische Grenzen reichen dafür aus.

Wenn Sie dagegen Objekte aus der Liste abrufen und sie stark als Kunden eingeben möchten, haben Sie zu Recht kein Glück. Da es sich bei der Liste um eine Liste handelt, List<Object>gibt es keine Garantie dafür, dass es sich bei den Inhalten um Kunden handelt. Daher müssen Sie beim Abrufen Ihr eigenes Casting bereitstellen. (Oder seien Sie wirklich, absolut, doppelt sicher, dass die Liste nur Customerseine Doppelbesetzung aus einer der anderen Antworten enthält und verwendet, aber stellen Sie fest, dass Sie die Typensicherheit zur Kompilierungszeit, die Sie von Generika erhalten, vollständig umgehen Fall).

Im Großen und Ganzen ist es immer gut, die größtmöglichen generischen Grenzen zu berücksichtigen, die beim Schreiben einer Methode akzeptabel wären, doppelt, wenn sie als Bibliotheksmethode verwendet werden soll. Wenn Sie nur aus einer Liste zu lesen , gehen, verwenden Sie List<? extends T>statt List<T>, zum Beispiel - das Ihre Anrufer gibt viel mehr Spielraum in den Argumenten sie und übermitteln können sie weniger wahrscheinlich in vermeidbare Probleme ähnlich dem laufen Sie‘ Ich bin hier.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.