Dies wird nicht kompiliert, jeder Vorschlag wird geschätzt.
...
List<Object> list = getList();
return (List<Customer>) list;
Compiler sagt: kann nicht werfen List<Object>
zuList<Customer>
Dies wird nicht kompiliert, jeder Vorschlag wird geschätzt.
...
List<Object> list = getList();
return (List<Customer>) list;
Compiler sagt: kann nicht werfen List<Object>
zuList<Customer>
Antworten:
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.
@SuppressWarnings("unchecked")
. Beachten Sie, dass Sie auch zu (List)
statt zu to senden können (Object)
.
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.
.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).
instanceof
Kunde ist?
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.
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());
FluentIterable
für mich gearbeitet.
Sie können eine Doppelbesetzung verwenden.
return (List<Customer>) (List) getList();
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.
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());
List<Customer> cusList = new ArrayList<Customer>();
for(Object o: list){
cusList.add((Customer)o);
}
return cusList;
list.stream().forEach(x->cusList.add((Customer)x))
return cuslist;
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 Object
in a umgewandelt Customer
und 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.
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.
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.
}
});
Ä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());
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 Customer
der 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 Customers
eine 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.