Java: Auf Null prüfen oder Ausnahmebehandlung zulassen

8

Ich wundere mich über die Kosten für die Verwendung eines try / exception, um Nullen zu behandeln, im Vergleich zur Verwendung einer if-Anweisung, um zuerst nach Nullen zu suchen.

Um mehr Informationen zur Verfügung zu stellen. Es gibt ein & gt; 50% Chance, Nullen zu bekommen, weil in dieser App. Es ist üblich, eine Null zu haben, wenn keine Daten eingegeben wurden ... daher ist es üblich, eine Berechnung unter Verwendung einer Null zu versuchen.

Würde das die Performance verbessern, wenn ich eine if-Anweisung vor der Berechnung zuerst auf Null überprüfe und die Berechnung gar nicht erst mache oder weniger kostet, die Ausnahme einfach auszulösen und damit umzugehen?

danke für irgendwelche Vorschläge: -)

Danke für das tolle, nachdenkliche Feedback! Hier ist ein PSEUDOcode-Beispiel, um die ursprüngliche Frage zu klären:

%Vor%     
AJay 11.07.2010, 16:14
quelle

6 Antworten

12

Ich würde empfehlen, auf null zu prüfen und nicht die Berechnung durchzuführen, anstatt eine Ausnahme auszulösen.

Eine Ausnahme sollte "außergewöhnlich" und selten sein, keine Möglichkeit, den Kontrollfluss zu steuern.

Ich würde Ihnen auch vorschlagen, einen Vertrag mit Ihren Kunden bezüglich der Eingabeparameter zu schließen. Wenn Nullen erlaubt sind, buchstabieren Sie es aus; Wenn dies nicht der Fall ist, machen Sie deutlich, was übergeben werden soll, Standardwerte und was Sie versprechen, wenn ein Nullwert übergeben wird.

    
duffymo 11.07.2010, 16:16
quelle
8

Wenn die Übergabe von null Argument ein Ausnahmefall ist, würde ich ein NullPointerException werfen.

%Vor%

Wenn die Übergabe von null ein erlaubter Fall ist, überspringe ich die Berechnung und gebe schließlich null zurück. Aber das macht meiner Meinung nach weniger Sinn. Das Übergeben von null in erster Instanz scheint ein Fehler im aufrufenden Code zu sein.

Was immer Sie wählen, sollte im Javadoc ordnungsgemäß dokumentiert werden, um Überraschungen für den API-Benutzer zu vermeiden.

    
BalusC 11.07.2010 16:21
quelle
3

Wenn es eine Chance von & gt; 50% gibt, eine Null zu bekommen, dann ist das kaum eine Ausnahme?

Persönlich würde ich sagen, dass wenn Sie erwarten, dass etwas passiert, Sie es entsprechend codieren sollten - in diesem Fall, nach Null zu suchen und alles zu tun, was angemessen ist. Ich habe es immer verstanden, eine Ausnahme zu machen, um nicht extrem billig zu sein, aber ich konnte es nicht mit Sicherheit sagen.

    
developmentalinsanity 11.07.2010 16:17
quelle
3

Versuch und Fang sind nah an "frei", aber Würfe können sehr teuer sein. Normalerweise optimieren VM-Ersteller keine Ausnahmepfade, da sie außergewöhnlich sind (vermutlich selten).

NullPointerException weist auf einen Programmierfehler (RuntimeException) hin und sollte nicht abgefangen werden. Anstatt die NullPointerException abzufangen, sollten Sie Ihren Code korrigieren, damit die Ausnahme nicht an erster Stelle eingefügt wird.

Schlimmer noch, wenn Sie die NullPointerException abfangen und ein anderer Teil des Berechnungscodes NullPointerException auslöst, als die, die Sie werfen wollen, haben Sie jetzt einen Fehler maskiert.

Um Ihre Frage vollständig zu beantworten, würde ich sie auf eine Art implementieren, sie profilieren und dann anders implementieren und profilieren ... dann würde ich nur die mit der if-Anweisung verwenden, die vermeidet, die NullPointerException unabhängig davon zu werfen das ist einfach wegen des obigen Punktes schneller.

    
TofuBeer 11.07.2010 17:46
quelle
0

Ich stimme den meisten anderen Antworten zu, dass Sie den Null-Check dem Try-Catch vorziehen sollten. Und ich habe einige von ihnen aufgewertet.

Aber Sie sollten versuchen, die Notwendigkeit so weit wie möglich zu vermeiden.

  

Es gibt ein & gt; 50% Chance, Nullen zu bekommen, weil in dieser App. Es ist üblich, eine Null zu haben, wenn keine Daten eingegeben wurden. Daher ist es üblich, eine Berechnung mit einer Null zu versuchen.

Das sollten Sie wirklich beheben.

Ermitteln Sie sinnvolle Standardwerte, die sicherstellen, dass die Berechnung funktioniert, oder vermeiden Sie den Aufruf einer Berechnung, ohne die erforderlichen Eingabedaten bereitzustellen.

Für viele der Standard-Datentypen und Berechnungen, die sie beinhalten, gibt es vernünftige Standardwerte. Standardnummern auf 0 oder 1, abhängig von ihrer Bedeutung, Standardzeichenfolgen und zu leerenden Sammlungen, und viele Berechnungen funktionieren einfach. Berücksichtigen Sie bei komplexeren Objekten, die Sie selbst erstellen, das Nullobjekt -Muster.

    
Don Roby 11.07.2010 18:33
quelle
0

Wenn Sie einen Fall haben, in dem ein Ergebnis oder eine Eingabe nicht von Ihrem Programm verarbeitet werden kann, dann sollte das ein Fehler sein. Sie sollten wissen, was Sie programmieren können und nur das erlauben. Im Hinblick auf mögliche zukünftige Fälle, in denen ein Ergebnis behandelt werden könnte, aber noch nicht, würde ich dennoch vorschlagen, es als einen Fehler zu betrachten, bis Sie tatsächlich dieses Ergebnis benötigen. Im Zweifelsfall wissen Sie nicht, das Programm weiß es nicht, es kann nicht bearbeitet werden, Sie haben Ihr Programm nicht damit ausgestattet, es zu handhaben, es ist also ein Fehler. Das Programm kann nicht mehr tun, als zu stoppen.

Für Benutzereingaben ist es eine sehr schlechte Idee, sich darauf zu verlassen, dass Ihr Programm irgendwann abstürzt. Sie wissen nicht wann oder ob es zum Absturz kommt. Es könnte am Ende das Falsche tun oder zehn Dinge zum Absturz bringen, die es nicht hätte tun sollen und nicht getan hätte, wenn die Eingaben validiert worden wären.

Was den Schutz vor Ihren eigenen Fehlern betrifft, ist das eher eine gemischte Sache. Sie sollten sich viel mehr darauf konzentrieren, sicherzustellen, dass die Dinge funktionieren, indem Sie Tests durchführen, Unbekannte eliminieren, Korrekturlesen durchführen und sicherstellen, dass Sie genau wissen, wie Ihr Programm funktioniert. Dennoch haben Sie gelegentlich Fälle, in denen die interne Verarbeitung zu unerwünschten Ergebnissen führt.

Wenn sich herausstellt, dass ein Ergebnis kein Fehler für einen bestimmten Fall ist, behandeln Sie die Ausnahme nicht. Sie behandeln null, keine Ausnahme. In diesem Fall ist das Ergebnis kein Fehler. Sie behandeln also das Ergebnis und nicht den Fehler. Es könnte nicht einfacher sein. Wenn Sie eine Ausnahme abfangen und dann etwas tun, was mit einem if wie zum Beispiel gemacht werden kann:

%Vor%

Dann ist das nicht richtig. Sie haben eine vollkommen akzeptable Vorgehensweise, wenn Sie das Extra nicht haben.

Dies ist viel besser und es hält Ihre Absicht ohne die extra Ausführlichkeit klar:

%Vor%

Beachten Sie, dass Sie nichts besonderes benötigen, um eine Ausnahme von einer anderen Ebene zu vermeiden. Wie ich Versuch, Fang oben zu setzen, ist eine schlechte Praxis. Sie sollten nur die Sache umwickeln, die eine Ausnahme auslöst:

%Vor%

Wenn Sie so darüber reden, wird nur null in Ausnahme und dann wieder zurück konvertiert. Dies ist Yo-Yo-Codierung.

Sie sollten mit:

beginnen %Vor%

Bis Sie tatsächlich die Handhabung für null implementieren können. Wenn Sie die Behandlung für die Ausnahme implementieren können, können Sie die Behandlung für die Null implementieren.

Wenn sich herausstellt, dass etwas nicht immer benötigt wird, fügen Sie entweder diese Methode hinzu oder ändern Sie i_need_it (wenn null immer behandelt wird):

%Vor%

Eine Alternative ist zu prüfen, ob es zuerst existiert:

%Vor%

Der Grund, warum dies nicht so oft gemacht wird, ist, dass es normalerweise so aussieht:

%Vor%

Dies neigt dazu, an Gleichzeitigkeitsproblemen anfälliger zu sein, kann mehr Anrufe erfordern, die unzuverlässig sein können (IO über Netzwerk), und kann ineffizient sein (zwei RTTs anstelle von einem). Andere Aufrufe werden oft wie folgt zusammengeführt, z. B. Aktualisierung, wenn vorhanden, Einfügen, wenn eindeutig, Aktualisieren, falls vorhanden oder Einfügen, usw., die dann zurückgeben, was normalerweise das Ergebnis wäre, statt anfänglich zu prüfen. Einige von ihnen haben Konflikte hinsichtlich der Nutzlastgrößen-Effizienz und der RTT-Effizienz, die auch aufgrund einer Reihe von Faktoren variieren können.

Es ist jedoch billiger, wenn Sie wechselndes Verhalten benötigen, wenn etwas existiert oder nicht, aber Sie müssen nicht daran arbeiten. Wenn Sie sich auch nicht um die oben genannten Bedenken kümmern müssen, ist es etwas klarer.

Vielleicht möchten Sie sogar:

%Vor%

Dies ist wieder nützlich, denn wenn Sie nur sicherstellen müssen, dass etwas existiert, ist es oft billiger als es zu bekommen. Es ist jedoch selten, dass Sie dies benötigen, da Sie in den meisten Fällen wissen wollen, ob etwas existiert, um direkt daran zu arbeiten.

Eine Möglichkeit, dies zu verstehen, ist, dass Sie 'does_it_exist' nicht veranlassen würden, eine Exception auszulösen, wenn sie einfach einen Boolean explizit in den Call-Stack zurückversetzen kann. 'i_want_it' ist ein kombiniertes 'has' und 'get'.

Während zwei getrennte Methoden die Methodensignaturen klarer voneinander trennen, müssen Sie manchmal von etwas anderem abweichen und der einfachste Weg dafür ist, wenn Sie nicht ein bisschen Zweideutigkeit haben:

%Vor%

Das ist besser, weil Sie den Vertrag in der Anrufkette abliefern, anstatt auf einem Sandhaus aufzubauen, das auf Fernwirkung basiert. Es gibt Möglichkeiten, Ihren Vertrag genauer zu übergeben, aber es neigt dazu, YAGNI (IE, übergeben Sie eine Methode Aufrufer) gefaltet.

Sie sollten Ausnahmen früh werfen und Sie wollen sicher sein, anstatt zu entschuldigen, so dass falsche Positive gut sind. Sobald Sie feststellen, dass es ein falsches positives ist, dann reparieren Sie es an der Quelle.

Es sollte äußerst selten sein, dass Sie Ausnahmen überhaupt behandeln. Die Mehrheit sollte die Spitze erreichen und dann einen Logging- und Ausgabe-Handler aufrufen. Den Rest behebst du entsprechend, indem du das Ergebnis direkt zurückgibst und handhabst. Wenn Sie von vielen Verwendungen nur ein falsches Positiv haben, verwenden Sie nur dieses. Entfernen Sie nicht einfach das Häkchen im Root und brechen Sie die vielen anderen Fälle, in denen es immer noch ein Fehler ist.

Java ist eine unglückliche Sprache, denn ich kann nicht sagen, dass man nicht null übergibt, oder diese Variable muss nicht null sein.

Wenn solch eine Funktion fehlt, ist es am besten, nach Nullen in ihren Quellen zu suchen, also nach Dingen wie IO, und nicht für jedes Mal, wenn etwas an eine Ihrer Methoden weitergegeben wird. Ansonsten ist das eine absurde Menge von Null-Überprüfung.

Sie können dieses Muster anwenden, um Funktionen zu erstellen, die Ihre ifs für die Parameterprüfung ersetzen, wenn Sie das wirklich brauchen. Sie würden ID durch das Objekt selbst ersetzen, wie zum Beispiel:

%Vor%

Dann:

%Vor%

Schade, dass es keinen Passthru-Typ gibt.

Oder:

%Vor%

Möglicherweise haben Sie einen bestimmten Getter und nicht einen generischen.

Oder:

%Vor%

Wenn Sie dies nur an der Grenze tun (wenn Sie Dinge außerhalb Ihres Steuerelements aufrufen, bei denen das Ergebnis ungleich null nicht garantiert werden kann), obwohl dies bevorzugt wird, oder wenn Sie eine dokumentierte Methode verwenden möchten als null zurückkehrend und nur dort, wo es wirklich nur gebraucht wird, bedeutet das, dass wenn du eine Null bekommst, wo sie nicht hingehört (Fehler passieren), wirst du es nicht leicht haben herauszufinden, wo sie herkommt . Es kann von IO sehr viel herumgereicht werden und keine Null-Zeiger-Ausnahme erzeugen, bis etwas versucht, darauf zu operieren. Solche Nullen können tagelang oder sogar monatelang als tickende Zeitbombe durch das System geleitet werden. "

Ich würde es nicht selbst tun, aber ich würde die Leute in einigen Fällen nicht beschuldigen, dass sie den Ansatz der defensiven Programmierung implementiert haben, der wegen des gebrochenen Systemtyps von Java erforderlich ist, das standardmäßig nicht erreichbar ist und nicht eingeschränkt werden kann. In stark typisierten Sprachen ist null nicht erlaubt, es sei denn, Sie sagen ausdrücklich, dass dies der Fall ist.

Bitte beachten Sie, dass ich, obwohl ich es kaputt mache, in der Regel seit Jahrzehnten deutlich lockere Sprachen verwende, um große, komplexe und kritische Systeme zu erstellen, ohne die Codebasis mit überflüssigen Überprüfungen zu überfrachten. Disziplin und Kompetenz bestimmen die Qualität.

Ein falsches positives Ergebnis ist, wenn ein Ergebnis oder eine Bedingung auftritt, von der Sie annehmen, dass es sich um ein Ergebnis handelt, das nicht von allen Anrufern verarbeitet werden kann. Es stellt sich jedoch heraus, dass mindestens ein Anrufer entsprechend damit umgehen kann. In diesem Fall behandeln Sie die Ausnahme nicht, sondern geben dem Aufrufer das Ergebnis. Es gibt sehr wenige Ausnahmen.

Java 8 hat optional, aber es sieht nicht wirklich hilfreich aus. Es ist ein schrecklicher Fall des inneren Plattformeffekts, der versucht, eine neue Syntax als Klassen zu implementieren und am Ende die Hälfte der existierenden Java-Syntax hinzufügen zu müssen, was sie sehr klobig macht. Wie üblich, löst modernes Java OOP jedes Problem von Leuten, die weniger Fudge wollen, indem sie mehr Fudge hinzufügen und Dinge komplizieren. Wenn Sie wirklich so verketten wollen, sollten Sie etwas wie Kotlin ausprobieren, das diese Syntax direkt implementiert.

Eine moderne Sprache bedeutet, dass Sie sich um das meiste nicht kümmern müssen und nur:

%Vor%

Eine moderne Sprache könnte sogar das ursprüngliche Objekt für eine Methode ohne Rückgabe zurückgeben (ersetzen Sie void durch self als Standard und automatische Rückkehr), damit die Leute ihre ausgefallene Verkettung oder was auch immer heute in Mode ist, programmieren können. p>

Sie können keine Factory mit einer Basisklasse haben, die Ihnen entweder Null oder NotNull gibt, weil Sie immer noch die Basisklasse bestehen müssen, und das ist eine Art Verletzung, wenn Sie sagen, dass Sie NotNull wollen.

Vielleicht möchten Sie mit Aspekten, statischen Analysen usw. experimentieren, obwohl das ein bisschen wie ein Kaninchenloch ist. Die gesamte Dokumentation sollte angeben, ob null zurückgegeben werden kann (obwohl, wenn es indirekt ist, kann es möglicherweise weggelassen werden).

Sie können eine Klasse wie MightHave machen, um Ihr Ergebnis zu verpacken, wo Sie Methoden wie get, require und haben können, wenn Sie keine Statik mögen, aber ein if essen wollen, obwohl es auch im Bereich von mild gewundenen und ist Mit all deinen Methodensignaturen herumzualbern, überall überall zu boxen, eine hässliche Lösung. Es ist nur als Alternative zu den seltenen Ausnahmefällen nützlich, in denen Ausnahmen aufgrund der Komplexität des Aufrufdiagramms und der Anzahl der Unbekannten, die über Ebenen verteilt sind, nützlich erscheinen.

Ein außergewöhnlich seltener Fall ist, wenn Ihre Quelle weiß, welche Ausnahme zu werfen ist, aber nicht, wenn sie geworfen werden muss, aber schwer zu überwinden ist (obwohl das Koppeln von zwei Schichten in einer Entfernung, wo irgendetwas dazwischen geschehen kann, vorsichtig angegangen werden muss ). Einige Leute möchten das auch, weil es leicht eine Spur von beiden geben kann, wo das fehlende Element herkam und wo es benötigt wurde, was etwas ist, das Checks möglicherweise nicht geben (sie werden wahrscheinlich in der Nähe der Quelle scheitern, aber nicht garantiert). Vorsicht ist geboten, da diese Art von Problemen eher auf eine schlechte Flusskontrolle oder übermäßige Komplexität hinweist als ein Problem mit gerechtfertigtem Polymorphismus, meta / dynamischer Codierung und ähnlichem.

Vorsicht ist geboten bei Dingen wie Standardeinstellungen oder dem Null-Objekt-Muster.Beides kann dazu führen, dass Fehler ausgeblendet werden und zu den besten Vermutungen oder einer Heilung werden, die schlimmer ist als die Krankheit. Dinge wie ein NullObject-Muster und Optional können oft verwendet werden, um die eingebaute Fehlerbehandlung in Ihrem Interpreter einfach abzuschalten oder zu umgehen.

Defaults sind nicht immer schlecht, können aber in den Bereich des Ratens fallen. Das Ausblenden von Fehlern durch den Benutzer endet damit, dass sie fehlschlagen. Standards (und Desinfektion) müssen immer sorgfältig durchdacht werden.

Dinge wie NullObject-Muster und Optional können überstrapaziert werden, um die Nullzeigerüberprüfung effektiv zu deaktivieren. Es macht einfach die Annahme, dass Null niemals ein Fehler ist, der mit Programmen endet, die etwas tun, nicht mit anderen, aber Sie wissen nicht was. In einigen Fällen kann dies zu urkomischen Ergebnissen führen, beispielsweise wenn die Kreditkarte des Benutzers ungültig ist. Stellen Sie sicher, dass Sie bei der Verwendung von Objekten, die Sie nicht verwenden, den gesamten Code in einen try-catch-Code einschließen, der eine Nullzeiger-Ausnahme ignoriert. Dies ist sehr häufig, da Personen dazu neigen, Fehler zu beheben, bei denen die Ausnahme ausgelöst wurde. In der Realität neigt der wahre Fehler dazu, weiter weg zu sein. Sie enden mit einer fehlerhaften E / A-Verarbeitung, die fälschlicherweise null zurückgibt, und diese Null wird im gesamten Programm weitergegeben. Anstatt diese eine Nullquelle zu reparieren, versuchen die Leute stattdessen, sie an allen Stellen zu reparieren, an denen sie einen Fehler verursacht.

NullObject-Muster oder MagicNoop-Muster haben ihren Platz, sind aber nicht zur allgemeinen Verwendung bestimmt. Sie sollten sie nicht verwenden, bis es sofort offensichtlich wird, dass sie in einer gerechtfertigten Weise nützlich sind, die nicht mehr Probleme verursachen wird, als sie löst. Manchmal ist ein Noop effektiv ein Fehler.

    
jgmjgm 27.02.2018 21:05
quelle

Tags und Links