it-swarm.dev

Beste Weg, um Nullen in Java zu behandeln?

Ich habe einen Code, der aufgrund einer NullPointerException fehlschlägt. Für das Objekt, bei dem das Objekt nicht vorhanden ist, wird eine Methode aufgerufen.

Dies führte mich jedoch dazu, über den besten Weg nachzudenken, dies zu beheben. Codiere ich immer defensiv für Nullen, damit ich zukunftssicheren Code für Nullzeigerausnahmen habe, oder sollte ich die Ursache für die Null beheben, damit sie nicht stromabwärts auftritt?.

Was sind deine Gedanken?

21
Shaun F

Wenn null ein vernünftiger Eingabeparameter für Ihre Methode ist, korrigieren Sie die Methode. Wenn nicht, beheben Sie den Anrufer. "Vernünftig" ist ein flexibler Begriff, daher schlage ich den folgenden Test vor: Wie sollte die Methode eine Null-Eingabe enthalten? Wenn Sie mehr als eine mögliche Antwort finden, ist null nicht eine vernünftige Eingabe.

45
user281377

Verwenden Sie nicht null, sondern Optional

Wie Sie bereits betont haben, besteht eines der größten Probleme mit null in Java darin, dass es überall verwendet werden kann, oder zumindest für alle Referenztypen.

Es ist unmöglich zu sagen, dass dies null sein könnte und was nicht.

Java 8 führt ein viel besseres Muster ein: Optional.

Und Beispiel von Oracle:

String version = "UNKNOWN";
if(computer != null) {
  Soundcard soundcard = computer.getSoundcard();
  if(soundcard != null) {
    USB usb = soundcard.getUSB();
    if(usb != null) {
      version = usb.getVersion();
    }
  }
}

Wenn jeder dieser Werte einen erfolgreichen Wert zurückgibt oder nicht, können Sie die APIs in Optionals ändern:

String name = computer.flatMap(Computer::getSoundcard)
    .flatMap(Soundcard::getUSB)
    .map(USB::getVersion)
    .orElse("UNKNOWN");

Durch die explizite Codierung der Optionalität im Typ werden Ihre Schnittstellen viel besser und Ihr Code wird sauberer.

Wenn Sie nicht Java 8) verwenden, können Sie sich com.google.common.base.Optional in Google Guava.

Eine gute Erklärung des Guava-Teams: https://github.com/google/guava/wiki/UsingAndAvoidingNullExplained

Eine allgemeinere Erklärung der Nachteile von null mit Beispielen aus mehreren Sprachen: https://www.lucidchart.com/techblog/2015/08/31/the-worst-mistake-of-computer-science/


@ Nonnull, @Nullable

Java 8 fügt diese Annotation hinzu, um Codeüberprüfungstools wie IDEs dabei zu unterstützen, Probleme zu erkennen. Sie sind in ihrer Wirksamkeit ziemlich begrenzt.


Überprüfen Sie, wann es Sinn macht

Schreiben Sie nicht 50% Ihres Codes und überprüfen Sie null, insbesondere wenn es nichts Sinnvolles gibt, das Ihr Code mit einem null -Wert tun kann.

Wenn andererseits null verwendet werden könnte und etwas bedeutet, stellen Sie sicher, dass Sie es verwenden.


Letztendlich können Sie null offensichtlich nicht aus Java entfernen. Ich empfehle dringend, die Abstraktion Optional nach Möglichkeit zu ersetzen und null zu überprüfen, wenn Sie zu anderen Zeiten etwas Vernünftiges dagegen tun können.

21
Paul Draper

Es gibt mehrere Möglichkeiten, damit umzugehen. Das Peppen Ihres Codes mit if (obj != null) {} ist nicht ideal, es ist chaotisch, es fügt Rauschen hinzu, wenn der Code später während des Wartungszyklus gelesen wird, und es ist fehleranfällig, da es leicht zu vergessen ist um diese Kesselplatte zu wickeln.

Dies hängt davon ab, ob der Code weiterhin leise ausgeführt werden soll oder fehlschlägt. Ist das null ein Fehler oder eine erwartete Bedingung?.

Was ist null

In jeder Definition und in jedem Fall steht Null für den absoluten Mangel an Daten. Nullen in Datenbanken stehen für das Fehlen eines Werts für diese Spalte. Eine Null String ist nicht dasselbe wie eine leere String, eine Null int ist theoretisch nicht dasselbe wie NULL. In der Praxis "kommt es darauf an". Leer String kann eine gute Null Object - Implementierung für die String-Klasse ergeben, für Integer hängt dies von der Geschäftslogik ab.

Alternativen sind:

  1. Das Null Object Muster. Erstellen Sie eine Instanz Ihres Objekts, die den Status null darstellt, und initialisieren Sie alle Verweise auf diesen Typ mit einem Verweis auf die Implementierung Null. Dies ist nützlich für Objekte mit einfachem Werttyp, die nicht viele Verweise auf andere Objekte haben, die ebenfalls null sein könnten, und von denen erwartet wird, dass sie null als gültiger Status sind.

  2. Verwenden Sie aspektorientierte Werkzeuge, um Methoden mit einem Null Checker - Aspekt zu weben, der verhindert, dass die Parameter null sind. Dies gilt für Fälle, in denen null ein Fehler ist.

  3. Verwenden Sie assert() nicht viel besser als if (obj != null){}, aber weniger Rauschen.

  4. Verwenden Sie ein Tool zur Vertragsdurchsetzung wie Verträge für Java . Gleicher Anwendungsfall wie AspectJ, jedoch neuer und verwendet Anmerkungen anstelle externer Konfigurationsdateien. Das Beste aus beiden Werken von Aspects und Asserts.

1 ist die ideale Lösung, wenn eingehende Daten als null bekannt sind und durch einen Standardwert ersetzt werden müssen, damit vorgelagerte Verbraucher nicht mit dem gesamten Nullprüfungs-Boilerplate-Code umgehen müssen. Das Überprüfen anhand bekannter Standardwerte ist ebenfalls aussagekräftiger.

2, 3 und 4 sind nur bequeme alternative Ausnahmegeneratoren, um NullPointerException durch etwas Informativeres zu ersetzen, was immer eine Verbesserung darstellt.

Am Ende

null in Java ist fast in allen Fällen ein logischer Fehler. Sie sollten immer versuchen, die Grundursache von NullPointerExceptions zu beseitigen. Sie sollten sich bemühen, dies nicht zu tun Verwenden Sie null Bedingungen als Geschäftslogik. if (x == null) { i = someDefault; } Nehmen Sie einfach die erste Zuweisung zu dieser Standardobjektinstanz vor.

8
user7519

Das Hinzufügen von Nullprüfungen kann das Testen problematisch machen. Sehen Sie diesen großartigen Vortrag ...

Lesen Sie den Vortrag über Google Tech-Vorträge: "The Clean Code Talks - Suchen Sie nicht nach Dingen!" er spricht ungefähr in Minute 24 darüber

http://www.youtube.com/watch?v=RlfLCWKxHJ0&list=PL693EFD059797C21E

Bei der paranoiden Programmierung werden überall Nullprüfungen hinzugefügt. Scheint zunächst eine gute Idee zu sein, macht es jedoch aus Testsicht schwierig, mit Ihren Null-Check-Typ-Tests umzugehen.

Auch wenn Sie eine Voraussetzung für die Existenz eines Objekts wie z

class House(Door door){

    .. null check here and throw exception if Door is NULL
    this.door = door
}

es verhindert, dass Sie House erstellen, da Sie eine Ausnahme auslösen. Angenommen, Ihre Testfälle erstellen Scheinobjekte zum Testen von etwas anderem als Tür. Sie können dies nicht tun, da eine Tür erforderlich ist

Diejenigen, die unter der Hölle der Scheinschöpfung gelitten haben, sind sich dieser Art von Ärger bewusst.

Zusammenfassend sollte Ihre Testsuite robust genug sein, um auf Türen, Häuser, Dächer oder was auch immer zu testen, ohne paranoid sein zu müssen. Seroiusly, wie schwierig ist es, einen Null-Check-Test für bestimmte Objekte in Ihren Tests hinzuzufügen :)

Sie sollten immer Anwendungen bevorzugen, die funktionieren, weil Sie mehrere Tests haben, die beweisen, dass es funktioniert, anstatt zu hoffen, dass es funktioniert, nur weil Sie überall eine ganze Reihe von vorbedingten Nullprüfungen haben

8
Constantin

tl; dr - Es ist GUT, nach unerwarteten nulls zu suchen, aber SCHLECHT, wenn eine Anwendung versucht, sie gut zu machen.

Details

Natürlich gibt es Situationen, in denen null eine gültige Eingabe oder Ausgabe für eine Methode ist, und andere, in denen dies nicht der Fall ist.

Regel # 1:

Das Javadoc für eine Methode, die einen null -Parameter zulässt oder einen null -Wert zurückgibt, muss dies klar dokumentieren und erklären was das null bedeutet.

Regel 2:

Eine API-Methode sollte nicht als Akzeptieren oder Zurückgeben von null angegeben werden, es sei denn, es gibt einen guten Grund dafür.

Bei einer klaren Spezifikation des "Vertrags" einer Methode gegenüber nulls ist es ein Programmierfehler , a zu übergeben oder zurückzugeben null wo du nicht solltest.

Regel # 3:

Eine Anwendung sollte nicht versuchen, Programmierfehler "gut zu machen".

Wenn eine Methode ein null erkennt, das nicht vorhanden sein sollte, sollte sie nicht versuchen, das Problem zu beheben, indem sie es in etwas anderes verwandelt. Das verbirgt das Problem nur vor dem Programmierer. Stattdessen sollte es die NPE zulassen und einen Fehler verursachen, damit der Programmierer die Grundursache herausfinden und beheben kann. Hoffentlich wird der Fehler beim Testen bemerkt. Wenn nicht, sagt dies etwas über Ihre Testmethode aus.

Regel 4:

Wenn möglich, schreiben Sie Ihren Code, um Programmierfehler frühzeitig zu erkennen.

Wenn Ihr Code Fehler enthält, die zu vielen NPEs führen, kann es am schwierigsten sein, herauszufinden, woher die null -Werte stammen. Eine Möglichkeit, die Diagnose zu vereinfachen, besteht darin, Ihren Code so zu schreiben, dass null so schnell wie möglich erkannt wird. Oft können Sie dies in Verbindung mit anderen Überprüfungen tun. z.B.

public setName(String name) {
    // This also detects `null` as an (intended) side-effect
    if (name.length() == 0) {
        throw new IllegalArgumentException("empty name");
    }
}

(Es gibt offensichtlich Fälle, in denen die Regeln 3 und 4 mit der Realität in Einklang gebracht werden sollten. Zum Beispiel (Regel 3) müssen einige Arten von Anwendungen versuchen, danach fortzufahren Wahrscheinlich Programmierfehler erkennen. Und (Regel 4) Eine zu starke Überprüfung auf schlechte Parameter kann sich auf die Leistung auswirken.)

4
Stephen C

Ich würde empfehlen, die Methode so zu reparieren, dass sie defensiv ist. Zum Beispiel:

String go(String s){  
    return s.toString();  
}

Sollte eher so aussehen:

String go(String s){  
    if(s == null){  
       return "";  
    }     
    return s.toString();  
}

Mir ist klar, dass dies absolut trivial ist, aber wenn der Aufrufer ein Objekt erwartet, geben Sie ihm ein Standardobjekt, das nicht dazu führt, dass eine Null herumgereicht wird.

3
Woot4Moo

Die folgenden allgemeinen Regeln zu Null haben mir bisher sehr geholfen:

  1. Wenn die Daten von außerhalb Ihrer Kontrolle stammen, suchen Sie systematisch nach Nullen und handeln Sie entsprechend. Dies bedeutet, dass entweder eine Ausnahme ausgelöst wird, die für die Funktion sinnvoll ist (aktiviert oder deaktiviert. Stellen Sie nur sicher, dass der Name der Ausnahme genau angibt, was gerade passiert.). Aber NIEMALS einen Wert in Ihrem System verlieren, der möglicherweise Überraschungen mit sich bringen kann.

  2. Wenn Null im Bereich der entsprechenden Werte für Ihr Datenmodell liegt, gehen Sie entsprechend damit um.

  3. Versuchen Sie bei der Rückgabe von Werten, nach Möglichkeit keine Nullen zurückzugeben. Bevorzugen Sie immer leere Listen, leere Zeichenfolgen und Nullobjektmuster. Behalten Sie Nullen als zurückgegebene Werte bei, wenn dies die bestmögliche Darstellung von Daten für einen bestimmten Anwendungsfall ist.

  4. Wahrscheinlich das wichtigste von allen ... Tests, Tests und erneut testen. Wenn Sie Ihren Code testen, testen Sie ihn nicht als Codierer. Testen Sie ihn als Domina eines Nazi-Psychopathen und versuchen Sie sich alle möglichen Möglichkeiten vorzustellen, um die Hölle aus diesem Code heraus zu quälen.

Dies ist ein wenig paranoid in Bezug auf Nullen, was häufig zu Fassaden und Proxys führt, die Systeme mit der Außenwelt verbinden, und zu streng kontrollierten Werten im Inneren mit reichlich Redundanz. Die Außenwelt hier bedeutet so ziemlich alles, was ich selbst nicht codiert habe. Es ist zwar kostenintensiv, aber bisher musste ich dies selten optimieren, indem ich "null sichere Abschnitte" des Codes erstellte. Ich muss jedoch sagen, dass ich meistens Systeme mit langer Laufzeit für das Gesundheitswesen erstelle. Das Letzte, was ich möchte, ist, dass das Schnittstellensubsystem, das Ihre Jodallergien gegen den CT-Scanner überträgt, aufgrund eines unerwarteten Nullzeigers abstürzt, weil jemand anderes in einem anderen System nie erkannt hat, dass Namen dies können enthalten Apostrophe oder Zeichen wie 但 耒耨。

sowieso .... meine 2 Cent

2
Newtopian

Ich würde vorschlagen, das Muster Option/Some/None aus funktionalen Sprachen zu verwenden. Ich bin kein Java Spezialist, aber ich verwende intensiv meine eigene Implementierung dieses Musters in meinem C # -Projekt, und ich bin sicher, dass es in Java] konvertiert werden könnte Welt.

Die Idee hinter diesem Muster ist: Wenn es logischerweise eine Situation gibt, in der die Möglichkeit besteht, dass ein Wert nicht vorhanden ist (z. B. beim Abrufen aus der Datenbank anhand der ID), geben Sie ein Objekt vom Typ Option [T] an, wobei T ein möglicher Wert ist . I Fall des Fehlens eines Wertobjekts der Klasse Keine [T] zurückgegeben, falls das Wertobjekt von Some [T] zurückgegeben wird, das den Wert enthält.

In diesem Fall müssen Sie die Möglichkeit der Wertabwesenheit behandeln, und wenn Sie eine Codeüberprüfung durchführen, können Sie leicht einen Ort mit falscher Behandlung finden. Informationen zur Implementierung der C # -Sprache finden Sie in meinem Bitbucket-Repository https://bitbucket.org/mikegirkin/optionsomenone

Wenn Sie einen Nullwert zurückgeben, der logisch einem Fehler entspricht (z. B. ist keine Datei vorhanden oder konnte keine Verbindung herstellen), sollten Sie eine Ausnahme auslösen oder ein anderes Fehlerbehandlungsmuster verwenden. Die Idee dahinter führt wiederum zu einer Lösung, wenn Sie mit der Situation der Wertabwesenheit umgehen müssen und leicht die Orte der falschen Behandlung finden können im Code.

1
Hedin
  1. Verwenden Sie den optionalen Typ nicht, es sei denn, er ist wirklich optional. Oft wird der Exit besser als Ausnahme behandelt, es sei denn, Sie haben wirklich null als Option erwartet, und nicht, weil Sie regelmäßig fehlerhaften Code schreiben.

  2. Das Problem ist nicht null als Typ, den es verwendet, wie der Artikel von Google hervorhebt. Das Problem ist, dass Nullen überprüft und behandelt werden sollten. Oft können sie ordnungsgemäß beendet werden.

  3. Es gibt eine Reihe von Nullfällen, die ungültige Bedingungen in Bereichen außerhalb des Routinebetriebs des Programms darstellen (ungültige Benutzereingaben, Datenbankprobleme, Netzwerkfehler, fehlende Dateien, beschädigte Daten), für die geprüfte Ausnahmen gelten und die sogar behandelt werden wenn es nur um es zu protokollieren.

  4. Bei der Ausnahmebehandlung sind andere betriebliche Prioritäten und Berechtigungen in der JVM zulässig als bei einem Typ wie Optional, der für die außergewöhnliche Art ihres Auftretens sinnvoll ist, einschließlich der frühzeitigen Unterstützung für das priorisierte verzögerte Laden des Handlers in den Speicher, da dies nicht der Fall ist brauche es die ganze Zeit herumhängen.

  5. Sie müssen nicht überall Null-Handler schreiben, nur dort, wo sie wahrscheinlich auftreten, überall dort, wo Sie möglicherweise auf unzuverlässige Datendienste zugreifen, und da die meisten davon in wenige allgemeine Muster fallen, die leicht abstrahiert werden können, benötigen Sie sie wirklich nur ein Handler-Anruf, außer in seltenen Fällen.

Ich denke also, meine Antwort wäre, sie in eine aktivierte Ausnahme zu packen und damit umzugehen oder den unzuverlässigen Code zu reparieren, wenn er innerhalb Ihrer Möglichkeiten liegt.

0
J-Boss

Nun, wenn eines der möglichen Ergebnisse Ihrer Methode ein Nullwert ist, sollten Sie dafür defensiv codieren, aber wenn eine Methode eine Nicht-Null zurückgeben soll, aber nicht, würde ich das sicher beheben.

Wie immer kommt es auf den Fall an, wie bei den meisten Dingen im Leben :)

0
jonezy

Die lang Bibliotheken von Apache Commons bieten eine Möglichkeit, mit Nullwerten umzugehen

mit der Methode defaultIfNull in der Klasse ObjectUtils können Sie einen Standardwert zurückgeben, wenn das übergebene Objekt null ist

0
Mahmoud Hossam