Gibt es einen eleganten Weg, um die erste nicht-null-Wert der multiplen-Methode gibt in Java?
Haben Sie schon gesehen, das viele Male selbst, ich bin mir sicher:
public SomeObject findSomeObject(Arguments args) {
SomeObject so = queryFirstSource(args); //the most likely source first, hopefully
if (so != null) return so;
so = querySecondSource(args); //a source less likely than the first, hopefully
if (so != null) return so;
so = queryThirdSource(args); //a source less likely than the previous, hopefully
if (so != null) return so;
//and so on
}
Wir haben verschiedene Quellen, wo ein Objekt, das wir suchen, könnte sein. Wie ein lebendiger Beispiel könnten wir ein image, dass wir zuerst prüfen, ob eine Benutzer-id in einer Liste der privilegierten Benutzer. Wenn nicht, überprüfen wir, ob die Benutzer-id ist in der Liste der zulässigen Benutzer. Sonst haben wir null zurück. (Es ist nicht das beste Beispiel, aber ich hoffe, es ist ein lebendiger genug.)
Guave bietet uns einige Helfer, die könnte zu verschönern, die obigen code:
public SomeObject findSomeObject(Arguments args) {
//if there are only two objects
return com.google.common.base.Objects.firstNonNull(queryFirstSource(args), querySecondSource(args));
//or else
return com.google.common.collect.Iterables.find(
Arrays.asList(
queryFirstSource(args)
, querySecondSource(args)
, queryThirdSource(args)
//, ...
)
, com.google.common.base.Predicates.notNull()
);
}
Aber, wie die erfahreneren unter uns werden schon gesehen haben, kann dies durchführen schlimm, wenn Sie die lookups (d.h. queryXXXXSource(args)
) eine bestimmte Menge Zeit. Das ist, weil wir jetzt die Abfrage alle Quellen zuerst und dann übergeben Sie die Ergebnisse der Methode, findet die ersten unter den Ergebnissen, die nicht null
.
Im Gegensatz zu dem ersten Beispiel, wo die nächste Quelle ist nur dann ausgewertet, wenn die ehemaligen nicht wieder etwas, diese zweite Lösung kann besser Aussehen, aber auf den ersten könnte durchführen viel schlimmer.
Hier kommen wir zu meiner eigentlichen Frage und zu denen ich schlage vor, etwas, dass ich hoffe, jemand hat bereits die Basis des es oder der, dass jemand vorschlagen könnte, eine noch ziepte Lösung.
Im Klartext: Hat schon jemand solches defferedFirstNonNull
(siehe unten) oder etwas ähnliches? Gibt es eine einfache plain-Java-Lösung zu erreichen, diese mit den neuen Stream Rahmen? Können Sie schlagen eine andere elegante Lösung, erreicht das gleiche Ergebnis?
Regeln: Java 8 erlaubt ist sowie aktiv gepflegt und gut bekannt Bibliotheken von Drittanbietern wie Google Guava oder Apache Commons Lang Apache-Lizenz oder ähnliches (Nicht GPL!).
Die vorgeschlagene Lösung:
public SomeObject findSomeObject(Arguments args) {
return Helper.deferredFirstNonNull(
Arrays.asList(
args -> queryFirstSource(args)
, args -> querySourceSource(args)
, args -> queryThirdSource(args)
)
, x -> x != null
)
}
Also die Methode defferedFirstNonNull
bewerten würde jeder lambda-Ausdruck nach dem anderen und sobald das Prädikat (x -> x != null
) wahr ist (D. H. haben wir eine übereinstimmung gefunden) die Methode liefert das Ergebnis sofort und würde nicht Abfragen weitere Quelle.
PS: ich weiß, dass die Ausdrücke args -> queryXXXXSource(args)
gekürzt werden kann, um queryXXXXSource
. Aber machen würde die vorgeschlagene Lösung schwieriger zu Lesen, da es nicht offensichtlich auf den ersten Blick, was geschehen wird.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Kommt es auf einige Faktoren, die Sie nicht definieren. Sie haben eine Feste, eher kleine Gruppe von
query…Source
Aktionen, wie gezeigt, in der Sie Ihre Frage oder sind Sie eher die Position, die eine flexible, erweiterbare Liste von Aktionen?Im ersten Fall könnten Sie in Erwägung ziehen, das
query…Source
Methoden, um eineOptional<SomeObject>
eher alsSomeObject
odernull
. Wenn Sie ändern Ihre Methoden werden wieKönnen Sie die Kette auf diese Weise:
Wenn Sie können Sie nicht ändern oder bevorzugen Sie zurück
null
können Sie immer noch dieOptional
Klasse:Wenn Sie sich für einen flexibleren Weg für eine größere Anzahl von Abfragen möglich, es unvermeidlich ist, konvertieren Sie Sie in eine Art Liste oder stream
Function
s. Eine mögliche Lösung ist:Dieser führt die gewünschte operation, aber es wird Komponieren die notwendigen Schritte, jedes mal, wenn Sie die Methode aufrufen. Wenn Sie aufrufen möchten, diese Methode öfter, sollten Sie Komponieren eine operation, die Sie wieder verwenden können:
Also Sie sehen, es gibt mehr als einen Weg. Und es kommt auf die eigentliche Aufgabe, welche Richtung zu gehen. Manchmal, auch das einfache
if
Sequenz angemessen sein könnte.Optional<>
Klasse und ich denke, es bietet die elegante Lösung für mein problem.Ja, gibt es:
Dies ist flexibler, da gibt es einen
Optional
nichtnull
im Falle einer nicht-null -source
gefunden wird.Bearbeiten: Wenn Sie möchten, lazy evaluation, die Sie verwenden sollten ein
Supplier
:Stream
von varargs, können Sie auch einfach anrufenStream.of(source1, source2, ...)
stattArrays.asList(source1, source2, ...).stream()
Ich würde es schreiben, wie hier (können Sie nicht brauchen Generika hier, aber warum nicht):
Und rufen Sie es mit:
(vorausgesetzt, Ihr
queryXXX
Methoden sind Instanzmethoden)Den Methoden, die eingesetzt werden, um bis eines einen Wert zurückgibt, passt das Prädikat (im Beispiel oben: gibt einen nicht-null-Wert).