practice - java== null




Vermeiden von!=Null-Anweisungen (20)

Wenn Nullwerte nicht zulässig sind

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

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

In der restlichen Methode wissen Sie dann, dass das object nicht null ist.

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

Beispiel:

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

Wenn Ihre Methode jedoch nur den Wert weitergibt und die nächste Methode den Wert weitergibt, kann dies problematisch werden. In diesem Fall können Sie das Argument wie oben prüfen.

Wenn null erlaubt ist

Das hängt wirklich davon ab. Wenn ich finde, dass ich oft so etwas mache:

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

Also verzweige ich und mache zwei völlig verschiedene Dinge. Es gibt kein hässliches Code-Snippet, da ich abhängig von den Daten zwei verschiedene Dinge tun muss. Soll ich zum Beispiel an der Eingabe arbeiten oder einen guten Standardwert berechnen?

Es ist eigentlich selten für mich, die Redewendung " if (object != null && ... " zu verwenden.

Es kann einfacher sein, Ihnen Beispiele zu geben, wenn Sie Beispiele zeigen, wo Sie normalerweise das Idiom verwenden.

Ich verwende object != null , um die NullPointerException zu vermeiden.

Gibt es eine gute Alternative dazu?

Zum Beispiel:

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

Dadurch wird eine NullPointerException , wenn unbekannt ist, ob das Objekt null oder nicht.

Beachten Sie, dass die akzeptierte Antwort möglicherweise nicht mehr aktuell ist. Weitere Informationen finden Sie unter https://stackoverflow.com/a/2386013/12943 .


Wenn undefinierte Werte nicht zulässig sind:

Sie können Ihre IDE so konfigurieren, dass Sie vor potenzieller Null-Dereferenzierung gewarnt wird. ZB in Eclipse siehe Voreinstellungen> Java> Compiler> Fehler / Warnungen / Nullanalyse .

Wenn undefinierte Werte zulässig sind:

Wenn Sie eine neue API definieren möchten, bei der undefinierte Werte sinnvoll sind , verwenden Sie das Optionsmuster (ist möglicherweise aus funktionalen Sprachen bekannt). Es hat folgende Vorteile:

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

Java 8 verfügt über eine eingebaute Optional Klasse (empfohlen). Für frühere Versionen gibt es Bibliotheksalternativen, zum Beispiel die Option Guava oder die Option FunctionalJava . Wie bei vielen Mustern im funktionalen Stil führt die Verwendung von Option in Java (sogar 8) zu einer gewissen Anzahl von Boilerplates, die Sie mit einer weniger ausführlichen JVM-Sprache (z. B. Scala oder Xtend) reduzieren können.

Wenn Sie mit einer API arbeiten müssen, die möglicherweise Nullen zurückgibt , können Sie in Java nicht viel tun. Xtend und Groovy haben den Elvis-Operator ?: Und den nullsicheren Dereferenzierungsoperator ?. Beachten Sie jedoch, dass dies im Falle einer Nullreferenz null zurückgibt, so dass die korrekte Behandlung von Null nur "verschoben" wird.


Abhängig von der Art der Objekte, die Sie überprüfen, können Sie möglicherweise einige der Klassen in den Apache Commons verwenden, z. B .: Apache Commons Lang und Apache Commons

Beispiel:

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

oder (je nachdem, 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 die 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 dieser statischen Klasse aus Spring (org.springframework.util.Assert)

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

Anstelle des Null-Objekt-Musters - das hat seine Verwendung - könnten Sie Situationen in Betracht ziehen, in denen das Null-Objekt ein Fehler ist.

Wenn die Ausnahme ausgelöst wird, überprüfen Sie die Stack-Ablaufverfolgung und gehen Sie den Fehler durch.


Das klingt für mich wie ein ziemlich allgemeines Problem, mit dem sich Junior- und Intermediate-Entwickler zu einem bestimmten Zeitpunkt konfrontiert sehen: Sie kennen die Verträge, an denen sie beteiligt sind, nicht oder vertrauen ihnen nicht, und sie überprüfen defensiv ihre Nullwerte. Wenn sie ihren eigenen Code schreiben, neigen sie dazu, NULL-Werte zurückzugeben, um etwas anzuzeigen, sodass der Anrufer nach NULL-Werten suchen muss.

Anders ausgedrückt: Es gibt zwei Fälle, in denen die Nullprüfung auftritt:

  1. Null ist eine gültige Antwort im Sinne des Vertrags; und

  2. Wo es keine gültige Antwort ist.

(2) ist einfach. Verwenden Sie entweder NullPointerException Anweisungen (Assertions) oder erlauben Sie Fehler (z. B. NullPointerException ). Assertionen sind eine stark unterbenutzte Java-Funktion, die in 1.4 hinzugefügt wurde. Die Syntax lautet:

assert <condition>

oder

assert <condition> : <object>

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

Eine assert Anweisung wirft einen Error ( AssertionError ) aus, wenn die Bedingung nicht erfüllt ist. Standardmäßig ignoriert Java Assertions. Sie können Zusicherungen aktivieren, indem Sie die Option -ea an die JVM übergeben. Sie können Assertions für einzelne Klassen und Pakete aktivieren und deaktivieren. Dies bedeutet, dass Sie den Code während der Entwicklung und des Tests mit den Assertions validieren und in einer Produktionsumgebung deaktivieren können, obwohl meine Tests kaum Auswirkungen auf die Performance von Assertions gezeigt haben.

Das Verwenden von Assertions in diesem Fall ist in Ordnung, da der Code einfach fehlschlägt. Dies ist der Fall, wenn Sie Assertions verwenden. Der einzige Unterschied besteht darin, dass es mit Behauptungen früher, sinnvoller und möglicherweise mit zusätzlichen Informationen geschehen kann, was Ihnen helfen kann, herauszufinden, warum es passiert, wenn Sie es nicht erwartet hätten.

(1) ist etwas schwieriger. Wenn Sie den Code, den Sie anrufen, nicht kontrollieren können, stecken Sie fest. Wenn null eine gültige Antwort ist, müssen Sie danach suchen.

Wenn es sich jedoch um Code handelt, den Sie kontrollieren (und dies ist häufig der Fall), ist es eine andere Geschichte. Vermeiden Sie die Verwendung von Nullen als Antwort. Mit Methoden, die Sammlungen zurückgeben, ist es ganz einfach: Leere Sammlungen (oder Arrays) anstelle von Nullen werden fast immer zurückgegeben.

Bei Nicht-Sammlungen könnte es schwieriger sein. Betrachten Sie dies als Beispiel: Wenn Sie über folgende Schnittstellen verfügen:

public interface Action {
  void doSomething();
}

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

Parser nimmt rohe Benutzereingaben entgegen und findet etwas zu tun, wenn Sie beispielsweise eine Befehlszeilenschnittstelle für etwas implementieren. Jetzt können Sie den Vertrag so einstellen, dass er null zurückgibt, wenn es keine geeignete Aktion gibt. Das führt zur Nullprüfung, über die Sie sprechen.

Eine alternative Lösung ist, niemals null zurückzugeben und stattdessen das Null-Objekt-Muster zu verwenden :

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 Sie:

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();

Dies ist ein viel besseres Design, da es zu prägnanterem Code führt.

Vielleicht ist es jedoch durchaus angebracht, dass die findAction () -Methode eine Exception mit einer aussagekräftigen Fehlermeldung auslöst - insbesondere in diesem Fall, wenn Sie auf Benutzereingaben angewiesen sind. Es wäre viel besser für die findAction-Methode, eine Exception auszulösen, als für die aufrufende Methode, die mit einer einfachen NullPointerException ohne Erklärung explodiert.

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, anstatt Do Nothing. Ihre Standardaktion sollte dem Benutzer eine Rückmeldung 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);
        }
    }
}

Ich bin ein Fan von "Fail-Fast" Code. Fragen Sie sich selbst - tun Sie etwas Nützliches, wenn der Parameter null ist? Wenn Sie keine klare Antwort darauf haben, was Ihr Code in diesem Fall tun soll ... Dh, er sollte anfangs niemals null sein, dann ignorieren Sie ihn und erlauben Sie das Auslösen einer NullPointerException. Der aufrufende Code macht eine NPE genauso verständlich wie eine IllegalArgumentException, es ist jedoch für den Entwickler einfacher zu debuggen und zu verstehen, was schiefgegangen ist, wenn eine NPE ausgelöst wird, anstatt dass Ihr Code versucht, eine andere unerwartete Kontingenz auszuführen Logik - was letztendlich dazu führt, dass die Anwendung trotzdem ausfällt.


Mit Java 8 steht die neue Klasse java.util.Optional , die einige der Probleme löst. Man kann zumindest sagen, dass es die Lesbarkeit des Codes verbessert und im Falle von öffentlichen APIs den Vertrag der API für den Client-Entwickler klarer 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 Fruit enthalten:

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();
}

Sehen Sie sich nun diesen Code an, in dem wir eine Fruit für eine bestimmte Fruit-Instanz suchen:

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

Mit dem map() Operator können Sie eine Berechnung an einem optionalen Objekt durchführen oder einen Wert daraus 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 / leere Werte immer noch notwendig, aber zumindest ist sich der Entwickler bewusst, dass der Wert leer ist und das Risiko des Vergessens der Überprüfung begrenzt ist.

In einer von Grund auf mit Hilfe von Optional API, wenn ein Rückgabewert leer ist und nur ein einfaches Objekt zurückgibt, wenn es nicht null (Konvention), kann der Clientcode die Überprüfung von einfachen Objektrückgabewerten auf Null zurücksetzen.

Natürlich könnte Optional auch als Methodenargument verwendet werden. In einigen Fällen ist dies eine bessere Möglichkeit, optionale Argumente als 5 oder 10 Überladungsmethoden anzugeben.

Optional bietet andere praktische Methoden, z. B. orElse , die die Verwendung eines Standardwerts zulassen, und ifPresent , das mit Lambda-Ausdrücken funktioniert.

Ich lade Sie ein, diesen Artikel (meine Hauptquelle für das Schreiben dieser Antwort) zu lesen, in dem die NullPointerException (und in der Regel der Nullzeiger) sowie die ( Optional ) Lösung von Optional gut erläutert sind: Java Optionale Objekte .


Nur für diese Situation -

Es wird nicht geprüft, ob eine Variable vor dem Aufruf einer equals-Methode null ist (ein Beispiel zum Vergleich von Zeichenfolgen):

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

führt zu einer NullPointerException wenn foo nicht vorhanden ist.

Sie können dies vermeiden, wenn Sie Ihre String wie folgt vergleichen:

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

Wow, ich hasse es fast, eine andere Antwort hinzuzufügen, wenn wir 57 verschiedene Möglichkeiten haben, das NullObject pattern zu empfehlen. Ich denke jedoch, dass einige an dieser Frage interessierte Personen gerne wissen NullObject pattern , dass es in der Tabelle einen Vorschlag gibt, der "null" hinzufügt -safe handling " - eine optimierte Syntax für die if-not-gleich-null-Logik.

Das von Alex Miller gegebene Beispiel sieht folgendermaßen aus:

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

Die ?. bedeutet, dass der linke Bezeichner nur aufgehoben wird, wenn er nicht null ist. Andernfalls wird der Rest des Ausdrucks als null ausgewertet. Einige Leute wie Java Posse-Mitglied Dick Wall und die Wähler von Devoxx lieben diesen Vorschlag sehr, aber es gibt auch Widerstand, da er die Verwendung von null als Wachposten-Wert tatsächlich fördern wird.

Update: Unter Project Coin wurde ein proposed für einen nullsicheren Operator in Java 7 eingereicht . Die Syntax unterscheidet sich ein wenig von dem obigen Beispiel, aber es ist derselbe Begriff.

Update: Der Vorschlag für einen nullsicheren Operator wurde nicht in die Projektmünze aufgenommen. Daher wird diese Syntax in Java 7 nicht angezeigt.


Darf ich es allgemeiner beantworten!

Wir in der Regel dieses Problem konfrontiert werden, wenn die Methoden , die Parameter in die Quere kommen wir nicht zu erwarten (schlechte Methodenaufruf ist Fehler des Programmierers). Zum Beispiel: Sie erwarten ein Objekt, stattdessen eine Null. Sie erwarten einen String mit mindestens einem Zeichen, stattdessen erhalten Sie einen leeren String ...

Es gibt also keinen Unterschied zwischen:

if(object == null){
   //you called my method badly!

}

oder

if(str.length() == 0){
   //you called my method badly again!
}

Beide möchten sicherstellen, dass wir gültige Parameter erhalten haben, bevor wir andere Funktionen ausführen.

Wie in einigen anderen Antworten erwähnt, können Sie das Muster nach Vertrag verfolgen, um die oben genannten Probleme zu vermeiden . Siehe http://en.wikipedia.org/wiki/Design_by_contract .

Um dieses Muster in Java zu implementieren, können Sie grundlegende Java-Annotationen wie javax.annotation.NotNull oder komplexere Bibliotheken wie Hibernate Validator verwenden .

Nur eine Probe:

getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)

Jetzt können Sie die Kernfunktion Ihrer Methode sicher entwickeln, ohne Eingabeparameter überprüfen zu müssen. Sie schützen Ihre Methoden vor unerwarteten Parametern.

Sie können einen Schritt weiter gehen und sicherstellen, dass in Ihrer Anwendung nur gültige Pojos erstellt werden können. (Probe von der Hibernate-Validierungsstelle)

public class Car {

   @NotNull
   private String manufacturer;

   @NotNull
   @Size(min = 2, max = 14)
   private String licensePlate;

   @Min(2)
   private int seatCount;

   // ...
}

Gemeinsames "Problem" in der Tat in Java.

Zuerst meine Gedanken dazu:

Ich denke, dass es schlecht ist, etwas zu "essen", wenn NULL übergeben wurde und NULL kein gültiger Wert ist. Wenn Sie die Methode nicht mit einem Fehler beenden, bedeutet dies, dass in Ihrer Methode nichts schiefgelaufen ist, was nicht der Fall ist. Dann geben Sie in diesem Fall wahrscheinlich Null zurück, und in der empfangenden Methode überprüfen Sie erneut nach Null, und es endet nie, und Sie erhalten das Ergebnis "Wenn! = Null" usw.

IMHO, null muss ein kritischer Fehler sein, der die weitere Ausführung verhindert (dh, wenn null kein gültiger Wert ist).

So löse ich dieses Problem:

Zuerst folge ich dieser Konvention:

  1. Alle öffentlichen Methoden / API prüfen ihre Argumente immer auf null
  2. Alle privaten Methoden prüfen nicht auf null, da es sich um kontrollierte Methoden handelt (lassen Sie die Ausnahme mit nullpointer aus, falls sie oben nicht behandelt wurde).
  3. Die einzigen anderen Methoden, die nicht auf Null prüfen, sind Dienstprogrammmethoden. Sie sind öffentlich, aber wenn Sie sie aus irgendeinem Grund anrufen, wissen Sie, welche Parameter Sie übergeben. Dies ist, als würde man versuchen, Wasser im Wasserkocher ohne Wasser zu kochen ...

Und schließlich lautet die erste Zeile der öffentlichen Methode im Code folgendermaßen:

ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();

Beachten Sie, dass addParam () self zurückgibt, sodass Sie weitere Parameter zur Überprüfung hinzufügen können.

Die Methode validate()wirft geprüft, ValidationExceptionwenn einer der Parameter null ist (aktiviert oder deaktiviert, ist eher ein Design- / Geschmacksproblem, aber mein ValidationExceptionwird geprüft).

void validate() throws ValidationException;

Die Nachricht enthält den folgenden Text, wenn beispielsweise "plans" null ist:

Msgstr " Ungültiger Argumentwert null für Parameter [plans] gefunden "

Wie Sie sehen, wird der zweite Wert in der addParam () - Methode (Zeichenfolge) für die Benutzermeldung benötigt, da Sie den übergebenen Variablennamen nicht ohne weiteres erkennen können, selbst wenn die Reflektion (sowieso nicht Gegenstand dieses Beitrags)

Und ja, wir wissen, dass wir jenseits dieser Linie keinen Wert mehr finden werden, also rufen wir einfach Methoden für diese Objekte auf.

Auf diese Weise ist der Code sauber, leicht zu pflegen und lesbar.


Letztendlich ist die einzige Möglichkeit, dieses Problem vollständig zu lösen, die Verwendung einer anderen Programmiersprache:

  • In Objective-C können Sie das Äquivalent eines Methodenaufrufs ausführen nil, und es wird absolut nichts passieren. Dies macht die meisten Nullprüfungen überflüssig, kann jedoch die Diagnose von Fehlern erheblich erschweren.
  • In Nice , einer von Java abgeleiteten Sprache, gibt es zwei Versionen aller Typen: eine potenziell Nullversion und eine Nichtnullversion. Sie können Methoden nur für Nicht-Null-Typen aufrufen. Potentiell-Null-Typen können durch explizite Überprüfung auf Null in Nicht-Null-Typen konvertiert werden. Dies macht es viel einfacher zu wissen, wo Nullprüfungen notwendig sind und wo sie nicht sind.

Die beste Alternative für Java 8 oder neuer ist die Verwendung der OptionalKlasse.

Optional stringToUse = Optional.of("optional is there");
stringToUse.ifPresent(System.out::println);

Dies ist besonders praktisch für lange Ketten möglicher Nullwerte. Beispiel:

Optional<Integer> i = Optional.ofNullable(wsObject.getFoo())
    .map(f -> f.getBar())
    .map(b -> b.getBaz())
    .map(b -> b.getInt());

Beispiel zum Auslösen einer Ausnahme auf null:

Optional optionalCarNull = Optional.ofNullable(someNull);
optionalCarNull.orElseThrow(IllegalStateException::new);

Java 7 führte die Objects.requireNonNullMethode ein, die nützlich sein kann, wenn etwas auf Nicht-NULL-Wert geprüft werden soll. Beispiel:

String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();

Dies ist ein sehr häufiges Problem für jeden Java-Entwickler. Daher gibt es in Java 8 offizielle Unterstützung, um diese Probleme ohne überladenen Code zu beheben.

Java 8 hat eingeführt java.util.Optional<T>. Es ist ein Container, der einen Wert ungleich Null enthalten kann oder nicht. Java 8 hat einen sichereren Weg für die Handhabung eines Objekts geschaffen, dessen Wert in einigen Fällen null sein kann. Es ist inspiriert von den Ideen von Haskell und Scala .

Kurz gesagt, die optionale Klasse enthält Methoden, um explizit mit den Fällen umzugehen, in denen ein Wert vorhanden ist oder nicht. Der Vorteil gegenüber Nullreferenzen besteht jedoch darin, dass Sie mit der optionalen Klasse <T> über den Fall nachdenken müssen, in dem der Wert nicht vorhanden ist. Folglich können Sie unbeabsichtigte Nullzeigerausnahmen verhindern.

In obigem Beispiel haben wir eine Heimservice-Fabrik, die einen Handle an mehrere in der Wohnung verfügbare Geräte zurückgibt. Diese Dienste sind jedoch möglicherweise nicht verfügbar / funktionsfähig. Dies bedeutet, dass es zu einer NullPointerException kommen kann. Anstatt ifvor der Verwendung eines Dienstes eine Nullbedingung hinzuzufügen, packen wir sie in Optional <Dienst> ein.

AUFRUFEN ZUR OPTION <T>

Betrachten wir eine Methode, um eine Referenz einer Dienstleistung von einer Fabrik zu erhalten. Geben Sie die Dienstreferenz nicht zurück, sondern umschließen Sie sie mit Optional. Der API-Benutzer wird darauf hingewiesen, dass der zurückgegebene Dienst möglicherweise defekt ist oder nicht verfügbar / funktionsfähig ist

public Optional<Service> getRefrigertorControl() {
      Service s = new  RefrigeratorService();
       //...
      return Optional.ofNullable(s);
   }

Wie Sie sehen, Optional.ofNullable()bietet es eine einfache Möglichkeit, die Referenz umwickelt zu bekommen. Es gibt andere Möglichkeiten, die Referenz von Optional zu erhalten, entweder Optional.empty()& Optional.of(). Eine für die Rückgabe eines leeren Objekts, anstatt null erneut abzustimmen, und die andere, um ein nicht-nullbares Objekt einzuwickeln.

Wie genau hilft es, eine Nullprüfung zu vermeiden?

Nachdem Sie ein Referenzobjekt umbrochen haben, bietet Optional viele nützliche Methoden zum Aufrufen von Methoden für eine umschlossene Referenz ohne NPE.

Optional ref = homeServices.getRefrigertorControl();
ref.ifPresent(HomeServices::switchItOn);

Optional.ifPresent ruft den angegebenen Consumer mit einer Referenz auf, wenn es sich nicht um einen Nullwert handelt. Ansonsten tut es nichts.

@FunctionalInterface
public interface Consumer<T>

Stellt eine Operation dar, die ein einzelnes Eingabeargument akzeptiert und kein Ergebnis zurückgibt. Im Gegensatz zu den meisten anderen funktionalen Schnittstellen wird erwartet, dass Consumer über Nebenwirkungen arbeitet. Es ist so sauber und leicht verständlich. HomeService.switchOn(Service)Wird im obigen Codebeispiel aufgerufen, wenn die Referenz für die optionale Aufbewahrung nicht null ist.

Wir verwenden den ternären Operator sehr häufig zum Überprüfen der Nullbedingung und geben einen alternativen Wert oder einen Standardwert zurück. Optional bietet eine andere Möglichkeit, dieselbe Bedingung zu behandeln, ohne NULL zu prüfen. Optional.orElse (defaultObj) gibt defaultObj zurück, wenn das Optional einen Nullwert hat. Verwenden wir dies in unserem Beispielcode:

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

Jetzt macht HomeServices.get () dasselbe, aber auf eine bessere Art und Weise. Es wird geprüft, ob der Dienst bereits initialisiert ist oder nicht. Ist dies der Fall, geben Sie dasselbe zurück oder erstellen Sie einen neuen neuen Dienst. Die optionale <T> .orElse (T) hilft dabei, einen Standardwert zurückzugeben.

Zum Schluss noch unser NPE-Code sowie null-check-free-Code:

import java.util.Optional;
public class HomeServices {
    private static final int NOW = 0;
    private static Optional<HomeServices> service;

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

public Optional<Service> getRefrigertorControl() {
    Service s = new  RefrigeratorService();
    //...
    return Optional.ofNullable(s);
}

public static void main(String[] args) {
    /* Get Home Services handle */
    Optional<HomeServices> homeServices = HomeServices.get();
    if(homeServices != null) {
        Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
        refrigertorControl.ifPresent(HomeServices::switchItOn);
    }
}

public static void switchItOn(Service s){
         //...
    }
}

Der komplette Post ist NPE sowie Null-Check-freier Code… Wirklich? .


Ich habe das ausprobiert NullObjectPatternaber für mich ist nicht immer der beste Weg zu gehen. Es gibt manchmal Situationen, in denen eine "keine Aktion" nicht angemessen ist.

NullPointerExceptionist eine Laufzeitausnahme , die besagt, dass der Entwickler fehlerhaft ist und mit genügend Erfahrung genau sagt, wo der Fehler liegt.

Nun zur Antwort:

Versuchen Sie, alle Ihre Attribute und ihre Zugriffselemente so privat wie möglich zu machen, oder vermeiden Sie es, sie den Clients überhaupt zugänglich zu machen. Sie können die Argumentwerte natürlich im Konstruktor haben, aber durch die Reduzierung des Gültigkeitsbereichs lassen Sie der Client-Klasse keinen ungültigen Wert übergeben. Wenn Sie die Werte ändern müssen, können Sie immer einen neuen erstellen object. Sie überprüfen die Werte im Konstruktor nur einmal und bei den übrigen Methoden können Sie fast sicher sein, dass die Werte nicht null sind.

Natürlich ist Erfahrung der bessere Weg, diesen Vorschlag zu verstehen und anzuwenden.

Byte!


Ich ignoriere die Antworten, die die Verwendung von Nullobjekten in jeder Situation suggerieren. Dieses Muster kann den Vertrag brechen und Probleme tiefer und tiefer vergraben, anstatt sie zu lösen, wobei nicht erwähnt wird, dass ein unsachgemäßer Gebrauch einen weiteren Stapel Boilerplate-Code erzeugt, der zukünftige Wartung erfordert.

Wenn etwas, das von einer Methode zurückgegeben wird, null sein kann und der aufrufende Code eine Entscheidung darüber treffen muss, sollte ein früherer Aufruf erfolgen, der den Status sicherstellt.

Denken Sie auch daran, dass das Null-Objekt-Muster bei Nichtbeachtung speicherhungrig ist. Zu diesem Zweck sollte die Instanz eines NullObjects von den Eigentümern gemeinsam genutzt werden und darf nicht für jeden dieser Objekte eine einzige Instanz sein.

Ich würde auch nicht empfehlen, dieses Muster zu verwenden, wenn der Typ eine primitive Typrepräsentation sein soll - wie mathematische Entitäten, die keine Skalare sind: Vektoren, Matrizen, komplexe Zahlen und POD-Objekte (Plain Old Data), die den Zustand enthalten sollen in Form von integrierten Java-Typen. Im letzteren Fall würden Sie Getter-Methoden mit beliebigen Ergebnissen aufrufen. Was sollte beispielsweise eine NullPerson.getName () -Methode zurückgeben?

Solche Fälle sollten in Betracht gezogen werden, um absurde Ergebnisse zu vermeiden.


Java 7 hat eine neue java.util.ObjectsDienstprogrammklasse, für die es eine requireNonNull()Methode gibt. Alles, was dies tut, ist ein werfen, NullPointerExceptionwenn sein Argument NULL ist, aber es bereinigt den Code etwas. Beispiel:

Objects.requireNonNull(someObject);
someObject.doCalc();

Die Methode ist am nützlichsten für die checking unmittelbar vor einer Zuweisung in einem Konstruktor, wobei bei jeder Verwendung drei Codezeilen gespeichert werden können:

Parent(Child child) {
   if (child == null) {
      throw new NullPointerException("child");
   }
   this.child = child;
}

wird

Parent(Child child) {
   this.child = Objects.requireNonNull(child, "child");
}

Null ist kein "Problem". Es ist ein wesentlicher Bestandteil eines complete Modellierungswerkzeugsatzes. Software zielt darauf ab, die Komplexität der Welt zu modellieren, und Null trägt ihre Last. Null bedeutet in Java und dergleichen "Keine Daten" oder "Unbekannt" . Daher ist es sinnvoll, für diese Zwecke Nullen zu verwenden. Ich ziehe das "Nullobjekt" -Muster nicht vor. Ich denke, es stellt sich das Problem „ Wer wird die Wächter bewachen “.
Wenn Sie mich fragen, wie meine Freundin heißt, sage ich Ihnen, dass ich keine Freundin habe. In der Java-Sprache gebe ich null zurück. Eine Alternative wäre, eine sinnvolle Ausnahme auszulösen, um auf ein Problem hinzuweisen, das nicht sein kann (oderSie möchten direkt dort gelöst werden und delegieren Sie sie an einen höheren Ort im Stapel, um den Benutzer erneut zu versuchen oder einen Datenzugriffsfehler zu melden.

  1. Für eine "unbekannte Frage" geben Sie "unbekannte Antwort" an. (Seien Sie nullsicher, wenn dies aus betriebswirtschaftlicher Sicht korrekt ist.) Wenn Argumente vor der Verwendung einmal vor einer Verwendung auf null überprüft werden, können mehrere Anrufer sie vor einem Anruf nicht überprüfen.

    public Photo getPhotoOfThePerson(Person person) {
        if (person == null)
            return null;
        // Grabbing some resources or intensive calculation
        // using person object anyhow.
    }
    

    Vorherige führt zu einem normalen Logikfluss, um kein Foto einer nicht existierenden Freundin aus meiner Fotobibliothek zu erhalten.

    getPhotoOfThePerson(me.getGirlfriend())
    

    Und es passt zu der neuen Java-API (wir freuen uns darauf)

    getPhotoByName(me.getGirlfriend()?.getName())
    

    Während es eher ein „normaler Geschäftsablauf“ ist, Fotos, die in der Datenbank gespeichert sind, für einige Personen nicht zu finden, habe ich in einigen anderen Fällen Paare wie unten verwendet

    public static MyEnum parseMyEnum(String value); // throws IllegalArgumentException
    public static MyEnum parseMyEnumOrNull(String value);
    

    Und verabscheuen Sie sich nicht <alt> + <shift> + <j>( tippen Sie Javadoc in Eclipse ein) und schreiben Sie drei zusätzliche Wörter für Ihre öffentliche API. Dies ist mehr als genug für alle, außer denen, die keine Dokumentation lesen.

    /**
     * @return photo or null
     */
    

    oder

    /**
     * @return photo, never null
     */
    
  2. Dies ist eher ein theoretischer Fall, und in den meisten Fällen sollten Sie die Java-sichere API (falls diese in weiteren 10 Jahren veröffentlicht wird) vorziehen, ist aber NullPointerExceptioneine Unterklasse von Exception. Es ist also eine Form Throwable, die auf Bedingungen hinweist, die eine vernünftige Anwendung möglicherweise einfangen möchte ( javadoc )! Um den ersten Vorteil von Ausnahmen zu nutzen und den Fehlerbehandlungscode von "regulärem" Code ( gemäß Java-Erstellern ) zu trennen , ist es wie für mich angebracht, zu fangen NullPointerException.

    public Photo getGirlfriendPhoto() {
        try {
            return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName());
        } catch (NullPointerException e) {
            return null;
        }
    }
    

    Fragen könnten aufkommen:

    F. Was ist, wenn getPhotoDataSource()null zurückgegeben wird?
    A. Es liegt an der Geschäftslogik. Wenn ich kein Fotoalbum finde, zeige ich Ihnen keine Fotos. Was ist, wenn appContext nicht initialisiert wird? Die Geschäftslogik dieser Methode macht das mit. Wenn die gleiche Logik strenger sein sollte und eine Ausnahme ausgelöst wird, ist dies Teil der Geschäftslogik, und die explizite Überprüfung auf null sollte verwendet werden (Fall 3). Die neue Java-Null-sichere API passt hier besser, um gezielt anzugeben, was impliziert und was nicht bedeutet, dass es im Falle von Programmierfehlern als ausfallsicher initialisiert werden soll.

    F. Redundanter Code könnte ausgeführt werden und unnötige Ressourcen könnten ergriffen werden.
    A: Es könnte geschehen, wenn Sie getPhotoByName()versuchen würden, eine Datenbankverbindung zu öffnen, PreparedStatementden Personennamen zuletzt als SQL-Parameter zu erstellen und zu verwenden. Der Ansatz für eine unbekannte Frage gibt hier eine unbekannte Antwort (Fall 1). Vor dem Zugriff auf Ressourcen sollte die Methode die Parameter überprüfen und bei Bedarf das Ergebnis "unbekannt" zurückgeben.

    F: Dieser Ansatz hat eine Leistungsstrafe aufgrund der versuchten Schließung.
    A. Software sollte zunächst leicht verständlich sein und modifiziert werden. Erst danach konnte über Leistung nachgedacht werden, und nur wenn nötig! und wo nötig ( source ) und viele andere).

    PS.Dieser Ansatz ist so vernünftig zu verwenden, da der separate Fehlerbehandlungscode vom Prinzip des "normalen" Codes an einigen Stellen angemessen ist. Betrachten Sie das nächste Beispiel:

    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        try {
            Result1 result1 = performSomeCalculation(predicate);
            Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
            Result3 result3 = performThirdCalculation(result2.getSomeProperty());
            Result4 result4 = performLastCalculation(result3.getSomeProperty());
            return result4.getSomeProperty();
        } catch (NullPointerException e) {
            return null;
        }
    }
    
    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        SomeValue result = null;
        if (predicate != null) {
            Result1 result1 = performSomeCalculation(predicate);
            if (result1 != null && result1.getSomeProperty() != null) {
                Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
                if (result2 != null && result2.getSomeProperty() != null) {
                    Result3 result3 = performThirdCalculation(result2.getSomeProperty());
                    if (result3 != null && result3.getSomeProperty() != null) {
                        Result4 result4 = performLastCalculation(result3.getSomeProperty());
                        if (result4 != null) {
                            result = result4.getSomeProperty();
                        }
                    }
                }
            }
        }
        return result;
    }
    

    PPS.Für diejenigen, die schnell absteigen (und nicht so schnell die Dokumentation lesen), möchte ich sagen, dass ich in meinem Leben noch nie eine Nullzeiger-Ausnahme (NPE) entdeckt habe. Diese Möglichkeit wurde jedoch von den Java-Erstellern absichtlich entworfen, da NPE eine Unterklasse von ist Exception. Wir haben einen Präzedenzfall in der Java-Geschichte, wenn dies nicht der Fall ThreadDeathist, Errorweil es sich tatsächlich um einen Anwendungsfehler handelt, sondern nur, weil es nicht beabsichtigt war, erwischt zu werden! Wie viel NPE passt, um Errorals zu sein ThreadDeath! Aber es ist nicht.

  3. Prüfen Sie, ob keine Daten vorhanden sind, nur wenn die Geschäftslogik dies impliziert.

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person != null) {
            person.setPhoneNumber(phoneNumber);
            dataSource.updatePerson(person);
        } else {
            Person = new Person(personId);
            person.setPhoneNumber(phoneNumber);
            dataSource.insertPerson(person);
        }
    }
    

    und

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person == null)
            throw new SomeReasonableUserException("What are you thinking about ???");
        person.setPhoneNumber(phoneNumber);
        dataSource.updatePerson(person);
    }
    

    Wenn appContext oder dataSource nicht initialisiert wird, wird die nicht behandelte Laufzeit von NullPointerException beendet und von Thread.defaultUncaughtExceptionHandler verarbeitet (damit Sie Ihren bevorzugten Protokollierer oder einen anderen Benachrichtigungsmechanismus definieren und verwenden können). Wenn nicht festgelegt, gibt ThreadGroup#uncaughtException Stacktrace an system err aus. Man sollte das Anwendungsfehlerprotokoll überwachen und das Jira-Problem für jede nicht behandelte Ausnahme öffnen, bei der es sich tatsächlich um Anwendungsfehler handelt. Der Programmierer sollte den Fehler irgendwo in der Initialisierung beheben.


Zusätzlich zur Verwendung können assertSie Folgendes verwenden:

if (someobject == null) {
    // Handle null here then move on.
}

Das ist etwas besser als:

if (someobject != null) {
    .....
    .....



    .....
}

public static <T> T ifNull(T toCheck, T ifNull) {
    if (toCheck == null) {
           return ifNull;
    }
    return toCheck;
}






null