Ein Webdienst gibt ein riesiges XML zurück und ich muss auf tief verschachtelte Felder davon zugreifen. Beispielsweise:
return wsObject.getFoo().getBar().getBaz().getInt()
Das Problem ist , dass getFoo()
, getBar()
, getBaz()
können alle zurück null
.
Wenn ich jedoch null
in allen Fällen nachschaue, wird der Code sehr ausführlich und schwer zu lesen. Außerdem kann es sein, dass ich die Schecks für einige Felder verpasse.
if (wsObject.getFoo() == null) return -1;
if (wsObject.getFoo().getBar() == null) return -1;
// maybe also do something with wsObject.getFoo().getBar()
if (wsObject.getFoo().getBar().getBaz() == null) return -1;
return wsObject.getFoo().getBar().getBaz().getInt();
Ist es akzeptabel zu schreiben?
try {
return wsObject.getFoo().getBar().getBaz().getInt();
} catch (NullPointerException ignored) {
return -1;
}
oder wäre das ein Antimuster?
java
exception
nullpointerexception
null
custom-error-handling
David Frank
quelle
quelle
null
Überprüfungen würden mir nicht so viel ausmachen, dawsObject.getFoo().getBar().getBaz().getInt()
es bereits nach Code riecht. Lesen Sie, was das "Gesetz von Demeter" ist, und ziehen Sie es vor, Ihren Code entsprechend umzugestalten. Dann ist auch das Problem mit dennull
Prüfungen verschwunden. Und denken Sie darüber nach, zu verwendenOptional
.wsdl2java
, der das Gesetz von Demeter nicht respektiert.Antworten:
Das Fangen
NullPointerException
ist sehr problematisch, da es fast überall passieren kann. Es ist sehr einfach, einen Fehler zu beheben, ihn versehentlich zu erkennen und so fortzufahren, als ob alles normal wäre, wodurch ein echtes Problem verborgen bleibt. Es ist so schwierig damit umzugehen, deshalb ist es am besten, es ganz zu vermeiden. (Denken Sie beispielsweise an das automatische Entpacken einer NullInteger
.)Ich schlage vor, dass Sie
Optional
stattdessen die Klasse verwenden. Dies ist häufig der beste Ansatz, wenn Sie mit vorhandenen oder fehlenden Werten arbeiten möchten.Damit könnten Sie Ihren Code folgendermaßen schreiben:
Warum optional?
Die Verwendung von
Optional
s anstelle vonnull
Werten, die möglicherweise nicht vorhanden sind, macht diese Tatsache für die Leser sehr sichtbar und klar, und das Typsystem stellt sicher, dass Sie sie nicht versehentlich vergessen.Sie erhalten auch Zugriff auf Methoden zum bequemeren Arbeiten mit solchen Werten, wie z
map
undorElse
.Ist Abwesenheit gültig oder Fehler?
Denken Sie aber auch darüber nach, ob es ein gültiges Ergebnis für die Zwischenmethoden ist, null zurückzugeben, oder ob dies ein Zeichen für einen Fehler ist. Wenn es sich immer um einen Fehler handelt, ist es wahrscheinlich besser, eine Ausnahme auszulösen, als einen speziellen Wert zurückzugeben, oder dass die Zwischenmethoden selbst eine Ausnahme auslösen.
Vielleicht mehr Optionen?
Wenn andererseits fehlende Werte aus den Zwischenmethoden gültig sind, können Sie möglicherweise zu wechseln
Optional
für sie s ?Dann könnten Sie sie so verwenden:
Warum nicht optional?
Der einzige Grund, warum ich daran denken kann, nicht zu verwenden,
Optional
ist, wenn dies ein wirklich leistungskritischer Teil des Codes ist und sich herausstellt, dass der Aufwand für die Speicherbereinigung ein Problem darstellt. Dies liegt daranOptional
, dass bei jeder Ausführung des Codes einige Objekte zugewiesen werden und die VM diese möglicherweise nicht optimieren kann. In diesem Fall könnten Ihre ursprünglichen Wenn-Tests besser sein.quelle
Try
stattdessen verwenden könnenOptional
. ObwohlTry
die Java-API keine enthält , gibt es viele Bibliotheken , die eine bereitstellen, z. B. javaslang.io , github.com/bradleyscollins/try4j , functionjava.org oder github.com/jasongoodwin/better-java-monadsFClass::getBar
etc wäre kürzer.static
Methode, die eine sehr geringe Strafe nach sich zieht.Ich schlage vor, darüber nachzudenken
Objects.requireNonNull(T obj, String message)
. Sie können Ketten mit einer detaillierten Nachricht für jede Ausnahme erstellen, zIch würde vorschlagen, dass Sie keine speziellen Rückgabewerte verwenden, wie z
-1
. Das ist kein Java-Stil. Java hat den Mechanismus der Ausnahmen entwickelt, um diesen altmodischen Weg zu vermeiden, der aus der C-Sprache stammt.Werfen
NullPointerException
ist auch nicht die beste Option. Sie können Ihre eigene Ausnahme angeben (indem Sie sie aktivieren, um sicherzustellen, dass sie von einem Benutzer behandelt wird, oder deaktivieren , um sie auf einfachere Weise zu verarbeiten) oder eine bestimmte Ausnahme vom von Ihnen verwendeten XML-Parser verwenden.quelle
Objects.requireNonNull
wirft schließlichNullPointerException
. Das macht die Situation also nicht anders alsreturn wsObject.getFoo().getBar().getBaz().getInt()
if
s, wie OP zeigteOptional
Klasse zu verwenden oder einen Nullwert zurückzugebenInteger
Unter der Annahme, dass die Klassenstruktur tatsächlich außerhalb unserer Kontrolle liegt, wie es der Fall zu sein scheint, halte ich es für eine vernünftige Lösung, die in der Frage vorgeschlagene NPE zu fangen, es sei denn, die Leistung ist ein Hauptanliegen. Eine kleine Verbesserung könnte darin bestehen, die Wurf- / Fanglogik zu verpacken, um Unordnung zu vermeiden:
Jetzt können Sie einfach tun:
quelle
return get(() -> wsObject.getFoo().getBar().getBaz().getInt(), "");
gibt keinen Fehler in der Kompilierungszeit, der problematisch sein könnte.Wie bereits von Tom im Kommentar erwähnt,
Die folgende Aussage verstößt gegen das Gesetz von Demeter .
Was Sie wollen, ist
int
und Sie können es bekommenFoo
. Das Gesetz von Demeter sagt, rede niemals mit den Fremden . Für Ihren Fall können Sie die eigentliche Implementierung unter der Haube vonFoo
und versteckenBar
.Jetzt können Sie erstellen Methode
Foo
zu holenint
ausBaz
. LetztendlichFoo
wirdBar
und in könnenBar
wir zugreifen,Int
ohneBaz
direkt ausgesetzt zu seinFoo
. Daher werden Nullprüfungen wahrscheinlich in verschiedene Klassen unterteilt, und nur die erforderlichen Attribute werden von den Klassen gemeinsam genutzt.quelle
null
Überprüfung ihrer eigenen Sub-Tags.Meine Antwort geht fast in die gleiche Zeile wie @janki, aber ich möchte das Code-Snippet wie folgt leicht ändern:
Sie können auch eine Nullprüfung hinzufügen
wsObject
, wenn die Wahrscheinlichkeit besteht, dass dieses Objekt null ist.quelle
Sie sagen, dass einige Methoden "möglicherweise zurückkehren
null
", sagen aber nicht, unter welchen Umständen sie zurückkehrennull
. Sie sagen, Sie fangen das,NullPointerException
aber Sie sagen nicht, warum Sie es fangen. Dieser Mangel an Informationen deutet darauf hin, dass Sie nicht genau wissen, wofür Ausnahmen gelten und warum sie der Alternative überlegen sind.Stellen Sie sich eine Klassenmethode vor, die eine Aktion ausführen soll, die Methode kann jedoch aufgrund von Umständen, die außerhalb ihrer Kontrolle liegen (was in der Tat für alle Methoden in Java der Fall ist), nicht garantieren , dass sie die Aktion ausführt . Wir nennen diese Methode und sie kehrt zurück. Der Code, der diese Methode aufruft, muss wissen, ob sie erfolgreich war. Wie kann es wissen? Wie kann es strukturiert werden, um mit den beiden Möglichkeiten von Erfolg oder Misserfolg umzugehen?
Mit Ausnahmen können wir Methoden schreiben, die als Nachbedingung erfolgreich sind . Wenn die Methode zurückgegeben wird, war sie erfolgreich. Wenn es eine Ausnahme auslöst, ist es fehlgeschlagen. Dies ist ein großer Gewinn für die Klarheit. Wir können Code schreiben, der den normalen Erfolgsfall klar verarbeitet, und den gesamten Fehlerbehandlungscode in
catch
Klauseln verschieben. Es stellt sich häufig heraus, dass die Details darüber, wie oder warum eine Methode nicht erfolgreich war, für den Aufrufer nicht wichtig sind, sodass dieselbecatch
Klausel für die Behandlung mehrerer Arten von Fehlern verwendet werden kann. Und es kommt oft vor, dass ein Verfahren nicht zu fangen Ausnahmen braucht sich überhaupt , kann aber nur erlauben sie zu propagieren seine Anrufer. Ausnahmen aufgrund von Programmfehlern sind in dieser letzteren Klasse; Nur wenige Methoden können angemessen reagieren, wenn ein Fehler vorliegt.Also diese Methoden, die zurückkehren
null
.null
Wert einen Fehler in Ihrem Code an? Wenn dies der Fall ist, sollten Sie die Ausnahme überhaupt nicht abfangen. Und Ihr Code sollte nicht versuchen, sich selbst zu erraten. Schreiben Sie einfach, was klar und prägnant ist, unter der Annahme, dass es funktionieren wird. Ist eine Kette von Methodenaufrufen klar und prägnant? Dann benutze sie einfach.null
Wert eine ungültige Eingabe in Ihr Programm an? Wenn dies der Fall ist,NullPointerException
ist a keine geeignete Ausnahme, da es herkömmlicherweise für die Anzeige von Fehlern reserviert ist. Sie möchten wahrscheinlich eine benutzerdefinierte Ausnahme auslösen, die vonIllegalArgumentException
(wenn Sie eine nicht aktivierte Ausnahme möchten ) oderIOException
(wenn Sie eine aktivierte Ausnahme möchten) abgeleitet ist. Muss Ihr Programm detaillierte Syntaxfehlermeldungen bereitstellen, wenn eine ungültige Eingabe vorliegt? Wenn ja, überprüfen Sie jede Methode auf anull
Rückgabewert dann eine entsprechende Diagnoseausnahme auslösen. Wenn Ihr Programm keine detaillierte Diagnose bereitstellen muss,NullPointerException
ist es am klarsten und präzisesten, die Methodenaufrufe miteinander zu verketten, alle abzufangen und dann Ihre benutzerdefinierte Ausnahme auszulösen.Eine der Antworten behauptet, dass die verketteten Methodenaufrufe die verletzen Gesetz von Demeterund daher schlecht sind. Diese Behauptung ist falsch.
quelle
Um die Lesbarkeit zu verbessern, möchten Sie möglicherweise mehrere Variablen verwenden, z
quelle
Nicht fangen
NullPointerException
. Sie wissen nicht, woher es kommt (ich weiß, dass es in Ihrem Fall nicht wahrscheinlich ist, aber vielleicht hat es etwas anderes geworfen) und es ist langsam. Sie möchten auf das angegebene Feld zugreifen und dafür muss jedes andere Feld nicht null sein. Dies ist ein perfekter Grund, jedes Feld zu überprüfen. Ich würde es wahrscheinlich in einem prüfen, wenn und dann eine Methode für die Lesbarkeit erstellen. Wie andere bereits betont haben, ist die Rückgabe von -1 sehr altmodisch, aber ich weiß nicht, ob Sie einen Grund dafür haben oder nicht (z. B. mit einem anderen System sprechen).Bearbeiten: Es ist fraglich, ob es gegen das Demeter-Gesetz verstößt, da das WsObject wahrscheinlich nur eine Datenstruktur ist (siehe https://stackoverflow.com/a/26021695/1528880 ).
quelle
Wenn Sie den Code nicht umgestalten möchten und Java 8 verwenden können, können Sie Methodenreferenzen verwenden.
Eine einfache Demo zuerst (entschuldigen Sie die statischen inneren Klassen)
Ausgabe
Die Schnittstelle
Getter
ist nur eine funktionale Schnittstelle. Sie können eine beliebige Entsprechung verwenden.GetterResult
Klasse, Accessororen aus Gründen der Klarheit entfernt, halten Sie das Ergebnis der Getter-Kette, falls vorhanden, oder den Index des zuletzt aufgerufenen Getter.Die Methode
getterChain
ist ein einfacher Code, der automatisch (oder bei Bedarf manuell) generiert werden kann.Ich habe den Code so strukturiert, dass der sich wiederholende Block offensichtlich ist.
Dies ist keine perfekte Lösung, da Sie immer noch eine Überladung
getterChain
pro Anzahl von Gettern definieren müssen.Ich würde stattdessen den Code umgestalten, aber wenn dies nicht möglich ist und Sie häufig lange Getterketten verwenden, können Sie eine Klasse mit Überladungen erstellen, die zwischen 2 und beispielsweise 10 Getter liegen.
quelle
Wie andere gesagt haben, ist die Einhaltung des Demeter-Gesetzes definitiv Teil der Lösung. Ein anderer Teil besteht, wo immer möglich, darin, diese verketteten Methoden so zu ändern, dass sie nicht zurückkehren können
null
. Sie können die Rückkehr vermeiden,null
indem Sie stattdessen ein leeresString
, ein leeresCollection
oder ein anderes Dummy-Objekt zurückgeben, das bedeutet oder tut, was auch immer der Aufrufer tun würdenull
.quelle
Ich möchte eine Antwort hinzufügen, die sich auf die Bedeutung des Fehlers konzentriert . Eine Null-Ausnahme an sich liefert keinen vollständigen Fehler. Ich würde daher raten, nicht direkt mit ihnen umzugehen.
Es gibt Tausende von Fällen, in denen Ihr Code schief gehen kann: Keine Verbindung zur Datenbank, E / A-Ausnahme, Netzwerkfehler ... Wenn Sie sie einzeln behandeln (wie hier die Nullprüfung), wäre dies zu mühsam.
Im Code:
Selbst wenn Sie wissen, welches Feld null ist, haben Sie keine Ahnung, was schief geht. Vielleicht ist Bar null, aber wird es erwartet? Oder ist es ein Datenfehler? Denken Sie an Leute, die Ihren Code lesen
Wie in der Antwort von xenteros würde ich vorschlagen, eine benutzerdefinierte, nicht aktivierte Ausnahme zu verwenden . Zum Beispiel in dieser Situation: Foo kann null sein (gültige Daten), aber Bar und Baz sollten niemals null sein (ungültige Daten)
Der Code kann neu geschrieben werden:
quelle
NullPointerException
ist eine Laufzeitausnahme, daher wird im Allgemeinen nicht empfohlen, sie abzufangen, sondern zu vermeiden.Sie müssen die Ausnahme abfangen, wo immer Sie die Methode aufrufen möchten (oder sie wird den Stapel weitergeben). Wenn Sie in Ihrem Fall mit diesem Ergebnis mit dem Wert -1 weiterarbeiten können und sicher sind, dass es sich nicht ausbreitet, weil Sie keine der "Teile" verwenden, die möglicherweise null sind, dann scheint es mir richtig zu sein fang es
Bearbeiten:
Ich stimme der späteren Antwort von @xenteros zu. Es ist besser, eine eigene Ausnahme zu starten, anstatt -1 zurückzugeben, die Sie beispielsweise aufrufen können
InvalidXMLException
.quelle
Verfolge diesen Beitrag seit gestern.
Ich habe die Kommentare kommentiert / abgestimmt, die besagen, dass es schlecht ist, NPE zu fangen. Hier ist, warum ich das getan habe.
Ausgabe
3.216
0,002
Ich sehe hier einen klaren Gewinner. Wenn Schecks viel zu teuer sind, als eine Ausnahme zu erwischen. Ich habe diese Java-8-Methode gesehen. In Anbetracht der Tatsache, dass 70% der aktuellen Anwendungen immer noch auf Java-7 ausgeführt werden, füge ich diese Antwort hinzu.
Endeffekt Für alle geschäftskritischen Anwendungen ist die Handhabung von NPE kostspielig.
quelle
Wenn Effizienz ein Problem ist, sollte die Option „Fang“ in Betracht gezogen werden. Wenn 'catch' nicht verwendet werden kann, weil es sich ausbreiten würde (wie von 'SCouto' erwähnt), verwenden Sie lokale Variablen, um mehrere Aufrufe von Methoden zu vermeiden
getFoo()
,getBar()
undgetBaz()
.quelle
Es lohnt sich, eine eigene Ausnahme zu erstellen. Nennen wir es MyOperationFailedException. Sie können es werfen, anstatt einen Wert zurückzugeben. Das Ergebnis ist das gleiche - Sie beenden die Funktion, geben jedoch keinen fest codierten Wert -1 zurück, bei dem es sich um ein Java-Anti-Pattern handelt. In Java verwenden wir Ausnahmen.
BEARBEITEN:
Lassen Sie mich gemäß der Diskussion in den Kommentaren etwas zu meinen vorherigen Gedanken hinzufügen. In diesem Code gibt es zwei Möglichkeiten. Zum einen akzeptieren Sie null und zum anderen ist es ein Fehler.
Wenn es sich um einen Fehler handelt und dieser auftritt, können Sie Ihren Code mithilfe anderer Strukturen für Debugging-Zwecke debuggen, wenn Haltepunkte nicht ausreichen.
Wenn es akzeptabel ist, ist es Ihnen egal, wo diese Null angezeigt wurde. Wenn Sie dies tun, sollten Sie diese Anfragen definitiv nicht verketten.
quelle
Die Methode, die Sie haben, ist langwierig, aber sehr gut lesbar. Wenn ich ein neuer Entwickler wäre, der zu Ihrer Codebasis kommt, könnte ich ziemlich schnell sehen, was Sie tun. Die meisten anderen Antworten (einschließlich des Abfangens der Ausnahme) scheinen die Dinge nicht lesbarer zu machen, und einige machen sie meiner Meinung nach weniger lesbar.
Da Sie wahrscheinlich keine Kontrolle über die generierte Quelle haben und davon ausgehen, dass Sie hier und da wirklich nur auf einige tief verschachtelte Felder zugreifen müssen, würde ich empfehlen, jeden tief verschachtelten Zugriff mit einer Methode zu versehen.
Wenn Sie feststellen, dass Sie viele dieser Methoden schreiben oder versucht sind, diese öffentlichen statischen Methoden zu erstellen, würde ich ein separates Objektmodell erstellen, das nach Ihren Wünschen verschachtelt ist und nur die Felder enthält, die Sie interessieren, und aus dem Web konvertieren Services-Objektmodell für Ihr Objektmodell.
Wenn Sie mit einem Remote-Webdienst kommunizieren, ist es sehr typisch, eine "Remote-Domäne" und eine "Anwendungsdomäne" zu haben und zwischen beiden zu wechseln. Die Remotedomäne wird häufig durch das Webprotokoll eingeschränkt (Sie können beispielsweise keine Hilfsmethoden in einem reinen RESTful-Dienst hin und her senden, und tief verschachtelte Objektmodelle sind üblich, um mehrere API-Aufrufe zu vermeiden) und daher nicht ideal für die direkte Verwendung in dein Klient.
Beispielsweise:
quelle
durch Anwendung des Gesetzes von Demeter,
quelle
Antwort geben, die sich von allen anderen zu unterscheiden scheint.
Grund :
Versuchen Sie dies, um die Bedingungen zu überprüfen, damit Ihr Code leicht lesbar ist:
EDIT:
Anregungen werden geschätzt .. !!
quelle
wsObject
enthält den vom Webservice zurückgegebenen Wert .. !! Der Service wird bereits aufgerufen undwsObject
erhält langeXML
Daten als Webservice-Antwort .. !! Es gibt also nichts Besseres als einen Server auf einem anderen Kontinent, denn esgetFoo()
handelt sich nur um ein Element, das die Getter-Methode erhält , nicht um einen Webservice-Aufruf. !! @xenterosIch habe eine Klasse namens geschrieben, mit
Snag
der Sie einen Pfad definieren können, um durch einen Baum von Objekten zu navigieren. Hier ist ein Beispiel für seine Verwendung:Dies bedeutet, dass die Instanz die an sie übergebene Instanz
ENGINE_NAME
effektiv aufruftCar?.getEngine()?.getName()
und zurückgibt,null
wenn eine Referenz zurückgegeben wirdnull
:Es ist nicht auf Maven veröffentlicht, aber wenn jemand dies nützlich findet, ist es hier (natürlich ohne Garantie!)
Es ist ein bisschen einfach, aber es scheint den Job zu machen. Offensichtlich ist es mit neueren Versionen von Java und anderen JVM-Sprachen, die eine sichere Navigation unterstützen, veraltet oder
Optional
.quelle