Frage Vermeiden! = Null-Anweisungen


ich benutze object != null viel zu vermeiden NullPointerException.

Gibt es dafür eine gute Alternative?

Beispielsweise:

if (someobject != null) {
    someobject.doCalc();
}

Dies vermeidet a NullPointerException, wenn es unbekannt ist, ob das Objekt ist null oder nicht.

Beachten Sie, dass die angenommene Antwort veraltet sein kann, siehe https://stackoverflow.com/a/2386013/12943 für einen neueren Ansatz.


3548


Ursprung


Antworten:


Das hört sich für mich nach einem relativ häufigen Problem an, das Junior- bis Intermediate-Entwickler zu einem bestimmten Zeitpunkt haben: Entweder kennen sie die Verträge, an denen sie teilnehmen, nicht oder vertrauen ihnen nicht, und sie checken defensiv nach Nullen. Wenn sie ihren eigenen Code schreiben, neigen sie außerdem dazu, Nullen zurückzugeben, um etwas anzuzeigen, so dass der Aufrufer nach Nullen suchen muss.

Um es anders auszudrücken, gibt es zwei Instanzen, bei denen die Nullprüfung stattfindet:

  1. Wo null ist eine gültige Antwort in Bezug auf den Vertrag; und

  2. Wo es keine gültige Antwort ist.

(2) ist einfach. Benutze entweder assert Anweisungen (Assertions) oder erlauben Fehler (zum Beispiel NullPointerException). Assertions sind ein stark unterausgelagertes Java-Feature, das in 1.4 hinzugefügt wurde. Die Syntax lautet:

assert <condition>

oder

assert <condition> : <object>

woher <condition> ist ein boolescher Ausdruck und <object> ist ein Objekt, dessen toString() Die Ausgabe der Methode wird in den Fehler eingeschlossen.

Ein assert Anweisung wirft ein Error (AssertionError) wenn die Bedingung nicht wahr ist. Standardmäßig ignoriert Java Assertions. Sie können Assertionen aktivieren, indem Sie die Option übergeben -ea an die JVM. Sie können Assertionen für einzelne Klassen und Pakete aktivieren und deaktivieren. Dies bedeutet, dass Sie Code während der Entwicklung und Prüfung mit den Assertionen validieren und in einer Produktionsumgebung deaktivieren können, obwohl meine Tests nahezu keine Leistungsbeeinträchtigung durch Assertions gezeigt haben.

In diesem Fall keine Assertionen zu verwenden, ist OK, weil der Code nur fehlschlägt, was passieren wird, wenn Sie Assertions verwenden. Der einzige Unterschied besteht darin, dass es bei Behauptungen früher, auf sinnvollere Weise und möglicherweise mit zusätzlichen Informationen geschehen kann, die Ihnen helfen können, herauszufinden, warum es passiert ist, wenn Sie es nicht erwartet haben.

(1) ist ein bisschen schwieriger. Wenn Sie keine Kontrolle über den Code haben, den Sie anrufen, stecken Sie fest. Wenn null eine gültige Antwort ist, müssen Sie nach es suchen.

Wenn es Code ist, den Sie jedoch kontrollieren (und das ist oft der Fall), dann ist das eine andere Geschichte. Vermeiden Sie Nullen als Antwort. Mit Methoden, die Sammlungen zurückgeben, ist es einfach: Liefern Sie leere Sammlungen (oder Arrays) anstelle von Nullen so oft wie möglich.

Bei Nicht-Sammlungen könnte es schwieriger sein. Betrachten Sie dies als ein Beispiel: Wenn Sie diese Schnittstellen haben:

public interface Action {
  void doSomething();
}

public interface Parser {
  Action findAction(String userInput);
}

Wo Parser rohe Benutzereingaben annimmt und etwas zu tun findet, etwa wenn Sie eine Befehlszeilenschnittstelle für etwas implementieren. Jetzt könnten Sie den Vertrag so abschließen, dass er null zurückgibt, wenn keine geeignete Aktion vorliegt. Das führt zur Nullkontrolle, von der du sprichst.

Eine alternative Lösung besteht darin, niemals null zurückzugeben und stattdessen den Wert " Null Objektmuster:

public class MyParser implements Parser {
  private static Action DO_NOTHING = new Action() {
    public void doSomething() { /* do nothing */ }
  };

  public Action findAction(String userInput) {
    // ...
    if ( /* we can't find any actions */ ) {
      return DO_NOTHING;
    }
  }
}

Vergleichen:

Parser parser = ParserFactory.getParser();
if (parser == null) {
  // now what?
  // this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {
  // do nothing
} else {
  action.doSomething();
}

zu

ParserFactory.getParser().findAction(someInput).doSomething();

Das ist ein viel besseres Design, weil es zu präziseren Code führt.

Das heißt, vielleicht ist es völlig angemessen, dass die findAction () -Methode eine Exception mit einer sinnvollen Fehlermeldung auslöst - insbesondere in diesem Fall, in dem Sie auf Benutzereingaben angewiesen sind. Es wäre viel besser für die Methode findAction, eine Exception auszulösen als für die aufrufende Methode, mit einer einfachen NullPointerException ohne Erklärung zu explodieren.

try {
    ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
    userConsole.err(anfe.getMessage());
}

Oder wenn Sie denken, dass der Try / Catch-Mechanismus zu hässlich ist, statt Do Nothing, sollte Ihre Standardaktion dem Benutzer Feedback geben.

public Action findAction(final String userInput) {
    /* Code to return requested Action if found */
    return new Action() {
        public void doSomething() {
            userConsole.err("Action not found: " + userInput);
        }
    }
}

2389



Wenn Sie eine Java IDE verwenden (oder planen) JetBrains IntelliJ IDEA, Eclipse oder Netbeans oder ein Tool wie Findbugs, dann können Sie Anmerkungen verwenden, um dieses Problem zu lösen.

Im Grunde hast du @Nullable und @NotNull.

Sie können in Methode und Parameter wie folgt verwenden:

@NotNull public static String helloWorld() {
    return "Hello World";
}

oder

@Nullable public static String helloWorld() {
    return "Hello World";
}

Das zweite Beispiel wird nicht kompiliert (in IntelliJ IDEA).

Wenn Sie den ersten verwenden helloWorld() Funktion in einem anderen Stück Code:

public static void main(String[] args)
{
    String result = helloWorld();
    if(result != null) {
        System.out.println(result);
    }
}

Jetzt wird der IntelliJ IDEA Compiler Ihnen sagen, dass die Überprüfung nutzlos ist, da der helloWorld() Funktion wird nicht zurückkehren null, je.

Parameter verwenden

void someMethod(@NotNull someParameter) { }

wenn du etwas schreibst wie:

someMethod(null);

Dies wird nicht kompiliert.

Letztes Beispiel mit @Nullable

@Nullable iWantToDestroyEverything() { return null; }

Dies tun

iWantToDestroyEverything().something();

Und Sie können sicher sein, dass dies nicht passieren wird. :)

Es ist eine gute Möglichkeit, den Compiler etwas mehr überprüfen zu lassen, als es normalerweise der Fall ist, und Ihre Verträge stärker zu machen. Leider wird es nicht von allen Compilern unterstützt.

In IntelliJ IDEA 10.5 und höher fügten sie Unterstützung für andere hinzu @Nullable  @NotNull Implementierungen.

Siehe Blogpost Flexibler und konfigurierbarer @ Nullable / @ NotNull Annotationen.


516



Wenn Null-Werte nicht erlaubt sind

Wenn Ihre Methode extern aufgerufen wird, beginnen Sie mit etwas wie diesem:

public void method(Object object) {
  if (object == null) {
    throw new IllegalArgumentException("...");
  }

Im Rest dieser Methode werden Sie das wissen object ist nicht null.

Wenn es sich um eine interne Methode handelt (nicht Teil einer API), dokumentieren Sie einfach, dass sie nicht null sein darf, und das war's.

Beispiel:

public String getFirst3Chars(String text) {
  return text.subString(0, 3);
}

Wenn Ihre Methode den Wert jedoch nur übergibt und die nächste Methode sie weitergibt usw., könnte dies problematisch werden. In diesem Fall sollten Sie das Argument wie oben überprüfen.

Wenn null erlaubt ist

Das hängt wirklich davon ab. Wenn Sie feststellen, dass ich oft so etwas tue:

if (object == null) {
  // something
} else {
  // something else
}

Also verzweifle ich und mache zwei völlig verschiedene Dinge. Es gibt kein hässliches Code-Snippet, weil ich abhängig von den Daten wirklich zwei verschiedene Dinge machen muss. Zum Beispiel, sollte ich an der Eingabe arbeiten oder sollte ich einen guten Standardwert berechnen?


Es ist wirklich selten für mich, das Idiom zu verwenden "if (object != null && ...".

Es mag einfacher sein, Ihnen Beispiele zu geben, wenn Sie Beispiele dafür zeigen, wo Sie normalerweise das Idiom verwenden.


282



Wow, ich hasse es fast, eine weitere Antwort hinzuzufügen, wenn wir 57 verschiedene Möglichkeiten haben, das zu empfehlen NullObject pattern, aber ich denke, dass einige Leute, die an dieser Frage interessiert sind, gerne wissen möchten, dass es einen Vorschlag auf dem Tisch für Java 7 gibt "null-sichere Handhabung"- eine optimierte Syntax für die Logik "Wenn-nicht-gleich-Null".

Das Beispiel von Alex Miller sieht so aus:

public String getPostcode(Person person) {  
  return person?.getAddress()?.getPostcode();  
}  

Das ?. bedeutet, dass nur der linke Bezeichner de-referenziert, wenn er nicht null ist, ansonsten den Rest des Ausdrucks als bewerten null. Einige Leute, wie Java Posse Mitglied Dick Wall und die Wähler bei Devoxx Ich mag diesen Vorschlag wirklich, aber es gibt auch eine Opposition, mit der Begründung, dass dies tatsächlich mehr Anreize für den Einsatz bietet null als Sentinel-Wert.


Aktualisieren: Ein offizieller Vorschlag für einen Null-Safe-Operator in Java 7 wurde unter eingereicht Projektmünze. Die Syntax ist ein wenig anders als das obige Beispiel, aber es ist die gleiche Idee.


Aktualisieren: Der Vorschlag des Null-sicheren Betreibers hat es nicht in Project Coin geschafft. Daher wird diese Syntax in Java 7 nicht angezeigt.


215



Wenn undefinierte Werte nicht erlaubt sind:

Sie könnten Ihre IDE so konfigurieren, dass Sie vor einer möglichen Null-Dereferenzierung gewarnt werden. Z.B. in Eclipse, siehe Einstellungen> Java> Compiler> Fehler / Warnungen / Null-Analyse.

Wenn undefinierte Werte erlaubt sind:

Wenn Sie eine neue API definieren möchten, bei der undefinierte Werte sinnvoll sind, benutze die Optionsmuster (kann aus funktionalen Sprachen vertraut sein). Es hat folgende Vorteile:

  • In der API wird explizit angegeben, ob eine Eingabe oder Ausgabe existiert oder nicht.
  • Der Compiler zwingt Sie, den Fall "undefined" zu behandeln.
  • Die Option ist eine MonadeDaher ist eine ausführliche Nullprüfung nicht erforderlich. Verwenden Sie einfach map / foreach / getOrElse oder einen ähnlichen Kombinator, um den Wert sicher zu verwenden (Beispiel).

Java 8 hat eine eingebaute Optional Klasse (empfohlen); Für frühere Versionen gibt es beispielsweise Bibliotheksalternativen Guaveist es Optional oder Funktionales Javaist es Option. Aber wie viele funktionale Stilmuster führt die Verwendung von Option in Java (sogar 8) zu einer ziemlich großen Zahl von Vorsätzen, die Sie mit einer weniger ausführlichen JVM-Sprache, z. Scala oder Xtend.

Wenn Sie mit einer API arbeiten müssen, die Nullen zurückgeben kann, Sie können nicht viel in Java tun. Xtend und Groovy haben die Elvis-Betreiber  ?: und das null-sicherer Dereferenzierungsoperator  ?.Beachten Sie jedoch, dass dies im Falle eines Nullverweises den Wert null zurückgibt, so dass die korrekte Behandlung von null nur "verschoben" wird.


177



Nur für diese Situation -

Es wird nicht überprüft, ob eine Variable null ist, bevor eine equals -Methode aufgerufen wird (Beispiel für ein Zeichenfolgenvergleich):

if ( foo.equals("bar") ) {
 // ...
}

wird zu a führen NullPointerException ob foo existiert nicht.

Das kannst du vermeiden, wenn du dein vergleichst Strings wie folgt:

if ( "bar".equals(foo) ) {
 // ...
}

160



Mit Java 8 kommt das Neue java.util.Optional Klasse, die wohl einige der Probleme löst. Man kann zumindest sagen, dass es die Lesbarkeit des Codes verbessert und im Falle öffentlicher APIs den Vertrag der API für den Client-Entwickler übersichtlicher macht.

Sie arbeiten so:

Ein optionales Objekt für einen bestimmten Typ (Fruit) wird als Rückgabetyp einer Methode erstellt. Es kann leer sein oder ein enthalten Fruit Objekt:

public static Optional<Fruit> find(String name, List<Fruit> fruits) {
   for (Fruit fruit : fruits) {
      if (fruit.getName().equals(name)) {
         return Optional.of(fruit);
      }
   }
   return Optional.empty();
}

Schauen Sie sich diesen Code an, wo wir eine Liste von suchen Fruit (fruits) für eine bestimmte Fruit-Instanz:

Optional<Fruit> found = find("lemon", fruits);
if (found.isPresent()) {
   Fruit fruit = found.get();
   String name = fruit.getName();
}

Du kannst den ... benutzen map() Operator, um eine Berechnung durchzuführen - oder einen Wert von einem optionalen Objekt zu extrahieren. orElse() können Sie einen Fallback für fehlende Werte bereitstellen.

String nameOrNull = find("lemon", fruits)
    .map(f -> f.getName())
    .orElse("empty-name");

Natürlich ist die Überprüfung auf Null / Leerwert immer noch notwendig, aber zumindest ist sich der Entwickler bewusst, dass der Wert leer sein könnte und das Risiko, das Überprüfen zu vergessen, begrenzt ist.

In einer von Grund auf neu erstellten API mit Optional wenn ein Rückgabewert leer sein sollte und ein einfaches Objekt nur zurückgegeben wird, wenn es nicht möglich ist null (Konvention), der Client-Code könnte Null-Checks für einfache Rückgabewerte von Objekten abbrechen ...

Na sicher Optional könnte auch als Methodenargument verwendet werden, vielleicht eine bessere Möglichkeit, optionale Argumente als 5 oder 10 Überlademethoden in einigen Fällen anzugeben.

Optional bietet andere bequeme Methoden, wie z orElse die erlauben, einen Standardwert zu verwenden, und ifPresent das funktioniert mit Lambda-Ausdrücke.

Ich lade Sie ein, diesen Artikel zu lesen (meine Hauptquelle für das Schreiben dieser Antwort), in der die NullPointerException (und im Allgemeinen Nullzeiger) problematisch sowie die (Teil -) Lösung von Optional sind gut erklärt: Java-optionale Objekte.


135



Je nachdem, welche Art von Objekten Sie überprüfen, können Sie möglicherweise einige der Klassen in den Apache-Commons verwenden: Apache Commons lang und Apache commons Sammlungen

Beispiel:

String foo;
...
if( StringUtils.isBlank( foo ) ) {
   ///do something
}

oder (abhängig davon, was Sie überprüfen müssen):

String foo;
...
if( StringUtils.isEmpty( foo ) ) {
   ///do something
}

Die StringUtils-Klasse ist nur eine von vielen. Es gibt einige gute Klassen in den Commons, die keine sichere Manipulation durchführen.

Hier folgt ein Beispiel, wie Sie in JAVA null vallidation verwenden können, wenn Sie die Apache-Bibliothek (commons-lang-2.4.jar) einschließen

public DOCUMENT read(String xml, ValidationEventHandler validationEventHandler) {
    Validate.notNull(validationEventHandler,"ValidationHandler not Injected");
    return read(new StringReader(xml), true, validationEventHandler);
}

Und wenn Sie Spring verwenden, hat Spring auch die gleiche Funktionalität in seinem Paket, siehe Bibliothek (spring-2.4.6.jar)

Beispiel zur Verwendung dieses statischen classf von spring (org.springframework.util.Assert)

Assert.notNull(validationEventHandler,"ValidationHandler not Injected");

113



  • Wenn Sie davon ausgehen, dass ein Objekt nicht null sein sollte (oder ein Fehler ist), verwenden Sie eine Assert.
  • Wenn Ihre Methode keine Nullparameter akzeptiert, sagen Sie sie im Javadoc und verwenden eine Assert.

Sie müssen nur nach Objekt! = Null suchen, wenn Sie den Fall behandeln wollen, in dem das Objekt null sein kann ...

Es gibt einen Vorschlag, neue Anmerkungen in Java7 hinzuzufügen, um mit null / notnull-Parametern zu helfen: http://tech.puredanger.com/java7/#jsr308


87