Vorwort
Nullzeiger sind die häufigsten und nervigsten Ausnahmen, die wir hassen. Um Null-Zeiger von Ausnahmen zu verhindern, dürfen Sie nicht viele Nicht-Null-Urteile in Ihren Kodex schreiben.
Java 8 führt eine neue optionale Klasse ein. Um das Auftreten von Nullzeiger zu vermeiden, müssen keine großen Anzahl von if(obj!=null) -Urteilen geschrieben werden, solange Sie die Daten in optional laden müssen, ein Container, der das Objekt umhüllt.
Es wird gesagt, dass kein Programmierer, der auf eine Null -Zeiger -Ausnahme gestoßen ist, kein Java -Programmierer ist und Null tatsächlich viele Probleme verursacht hat. Java 8 führt eine neue Klasse namens java.util.Optional vor, um viele durch Null verursachte Probleme zu vermeiden.
Mal sehen, was eine Nullreferenz schaden kann. Erstellen Sie zunächst einen Klassencomputer, die Struktur ist in der folgenden Abbildung dargestellt:
Was passiert, wenn wir den folgenden Code anrufen?
String Version = computer.getSoundCard (). GetUSB (). Getversion ();
Der obige Code scheint in Ordnung zu sein, aber viele Computer (wie Raspberry Pi) haben tatsächlich keine Soundkarten. Wenn Sie also getSoundcard() anrufen, wird eine Null -Zeigerausnahme auf jeden Fall ausgelöst.
Eine regelmäßige, aber schlechte Methode besteht darin, einen Null -Referenz zurückzugeben, um anzuzeigen, dass der Computer keine Soundkarte hat. Dies bedeutet jedoch, dass die Methode von GetUSB () auf eine leere Referenz aufgerufen wird, die offensichtlich eine Kontrollausnahme während des Programms ausführt und das Programm zum Stillstand zum Ausführen bringt. Denken Sie darüber nach, wie peinlich es ist, plötzlich zu erscheinen, wenn Ihr Programm auf einem Client -Computer ausgeführt wird?
Die große Informatik, Tony Hoare, schrieb einmal: "Ich denke, Null -Zitate wurden 1965 geschaffen, was zu einer Milliarde Dollar an Verlusten führte.
Wie können wir also Null -Zeiger -Ausnahmen vermeiden, wenn das Programm ausgeführt wird? Sie müssen wachsam sein und ständig nach möglichen Nullzeiger wie folgt überprüfen:
String Version = "unbekannt"; if (computer! if (Soundcard! = null) {USB USB = Soundcard.getUSB (); if (usb! = null) {Version = USB.Getversion (); }}} Sie können jedoch sehen, dass der obige Code zu viele Nullprüfungen enthält und die gesamte Codestruktur sehr hässlich wird. Wir müssen dieses Urteil jedoch verwenden, um sicherzustellen, dass es keine Nullzeiger gibt, wenn das System ausgeführt wird. Es ist einfach ärgerlich zu beurteilen, ob es in unserer Geschäftsordnung viele solchen leeren Referenzen gibt, und es führt auch zu einer schlechten Lesbarkeit unseres Code.
Wenn Sie vergessen zu überprüfen, ob der Wert leer ist, haben Null -Referenzen auch große potenzielle Probleme. In diesem Artikel werde ich beweisen, dass die Verwendung von Nullreferenzen als Darstellung, bei der Werte nicht existieren, ein schlechter Weg ist. Wir brauchen ein besseres Modell, das angibt, dass der Wert nicht existiert, anstatt NULL -Referenzen erneut zu verwenden.
Java 8 führte eine neue Klasse namens java.util.Optional<T> vor, die von der Sprache von Haskell und der Scala -Sprache inspiriert wurde. Diese Klasse kann einen willkürlichen Wert enthalten, wie in Abbildung und Code unten gezeigt. Sie können sich optional als einen Wert vorstellen, der einen Wert enthalten kann. Wenn optional keinen Wert enthält, ist er leer, wie in der folgenden Abbildung gezeigt.
Public Class Computer {private optionale <soundcard> Soundcard; public optional <soundcard> gotsoundcard () {...} ...} öffentliche Klasse Soundcard {private optional <USB> USB; public optional <sb> getUSB () {...}} öffentliche Klasse USB {public String getversion () {...}} Der obige Code zeigt, dass ein Computer eine Tonkarte ersetzen kann (die Tonkarte kann möglicherweise nicht vorhanden). Die Soundkarte kann auch einen USB -Anschluss enthalten. Dies ist eine Verbesserungsmethode, und das Modell kann deutlicher widerspiegeln, dass ein bestimmter Wert möglicherweise nicht existiert.
Aber wie geht es mit dem Optional<Soundcard> -Objekt um? Schließlich möchten Sie die USB -Portnummer erhalten. Es ist sehr einfach. Die optionale Klasse enthält einige Methoden, um sich mit dem Wert zu befassen. Im Vergleich zu Null -Referenzen zwingt Sie die optionale Klassenklasse, um zu verarbeiten, ob der Wert miteinander verbunden ist, und vermeidet so Ausnahmen von Nullzeiger.
Es ist zu beachten, dass die optionale Klasse keine Nullreferenzen ersetzt. Um die entworfene API leichter zu verstehen, können Sie feststellen, ob der Wert, der an die Funktion übergeben wird, möglicherweise nicht existiert, um die Signatur einer Funktion zu verstehen. Auf diese Weise werden Sie aufgefordert, die optionale Klasse zu öffnen, um den tatsächlichen Wert zu verarbeiten.
Optionalen Modus annehmen
Nachdem wir so viel gesagt haben, werfen wir einen Blick auf einen Code! Schauen wir uns zunächst an, wie Sie optional verwendet werden, um die traditionelle Null -Referenzerkennung neu zu schreiben. Am Ende dieses Artikels verstehen Sie, wie Sie optional verwendet werden.
String name = computer.flatMap (computer :: gotsoundCard) .flatMap (SoundCard :: getUSB) .MAP (USB :: Getversion) .Orelse ("unbekannt"); Erstellen Sie optionale Objekte
Ein leeres optionales Objekt kann erstellt werden:
Optional <soundcard> sc = optional.Empty ();
Als nächstes erstellen Sie eine optionale, die nicht-null-Werte enthält:
Soundcard SoundCard = new Soundcard (); optional <SoundCard> sc = optional.of (Soundcard);
Wenn die Soundkarte null ist, wird die Null -Zeiger -Ausnahme sofort ausgelöst (dies ist besser, als sie nur zu werfen, wenn Sie das Attribut der Soundkarte erhalten).
Durch die Verwendung von nullable können Sie ein optionales Objekt erstellen, das möglicherweise Nullreferenzen enthält:
Optional <Soundcard> sc = optional.ofnullable (Soundcard);
Wenn die Schallkarte eine Nullreferenz ist, ist das optionale Objekt leer.
Verarbeitung von Werten in optional
Nachdem ein optionales Objekt vorhanden ist, können Sie die entsprechende Methode aufrufen, um zu verarbeiten, ob der Wert im optionalen Objekt vorhanden ist. Im Vergleich zur Null -Erkennung können wir die IFPresent () -Methode wie folgt verwenden:
Optional <soundcard> Soundcard = ...; Soundcard.ifpresent (System.out :: println);
Auf diese Weise müssen keine Null -Erkennung durchgeführt werden. Wenn das optionale Objekt leer ist, werden keine Informationen gedruckt.
Sie können auch die Methode isPresent() verwenden, um festzustellen, ob das optionale Objekt wirklich vorhanden ist. Darüber hinaus gibt es auch eine GET () -Methode, die die enthaltenen Werte im optionalen Objekt zurückgibt, falls vorhanden. Andernfalls wird eine NoSuchelementException geworfen. Diese beiden Methoden können zusammen wie folgt verwendet werden, um Ausnahmen zu vermeiden:
if (Soundcard.ispresent ()) {System.out.println (Soundcard.get ());} Diese Methode wird jedoch nicht empfohlen (sie hat im Vergleich zur Null -Erkennung keine Verbesserung). Im Folgenden werden wir die üblichen Arbeitsweisen diskutieren.
Gibt Standardwerte und verwandte Operationen zurück
Bei der Begegnung mit Null soll ein regelmäßiger Betrieb einen Standardwert zurücksenden, den Sie mit ternären Ausdrücken zur Implementierung verwenden können:
Soundcard Soundcard = MaybeSoundCard! = NULL? MaybeSoundCard: New SoundCard ("Basic_sound_card"); Wenn Sie ein optionales Objekt verwenden, können Sie orElse() zum Überschreiben verwenden. Wenn optional ist, kann orElse() einen Standardwert zurückgeben:
Soundcard Soundcard = maybeSoundCard.orelse (neue Soundcard ("Defaut"); In ähnlicher Weise kann Orelsethrow (), wenn optional leer ist, ausgenommen werden:
Soundcard Soundcard = maybeSoundCard.orelsethrow (IllegalStateException :: New);
Verwenden Sie den Filter, um bestimmte Werte zu filtern
Wir nennen oft eine Objektmethode, um ihre Eigenschaften zu beurteilen. Sie müssen beispielsweise prüfen, ob die USB -Portnummer ein bestimmter Wert ist. Aus Sicherheitsgründen müssen Sie prüfen, ob der medizinische Gebrauch auf USB null ist, und dann getVersion() wie folgt aufrufen:
USB USB = ...; if (USB!
Wenn Sie optional verwenden, können Sie die Filterfunktion zum Umschreiben verwenden:
Optional <sb> vielleichtUsB = ...; vielleichtUsB.Filter (USB -> "3.0" .Equals (USB.Getversion ()) .ifpresent (() -> System.out.println ("OK")); Die Filtermethode erfordert ein Gegenteil als Parameter. Wenn der Wert des optionalen Werts vorhanden ist und die Vorhersage erfüllt, gibt die Filterfunktion einen Wert zurück, der die Bedingung erfüllt. Andernfalls wird ein leeres optionales Objekt zurückgegeben.
Verwenden Sie die Kartenmethode, um Daten zu extrahieren und zu konvertieren
Ein gemeinsames Muster besteht darin, einige Eigenschaften eines Objekts zu extrahieren. Für ein Soundcard -Objekt müssen Sie beispielsweise sein USB -Objekt erhalten und dann die Versionsnummer bestimmen. Normalerweise ist unsere Implementierung so:
if (Soundcard! = null) {USB USB = Soundcard.getUSB (); if (USB! Wir können mit der MAP -Methode diese Erkennungsnull überschreiben und dann das Objekt des Objekttyps extrahieren.
Optional <sb> USB = MaybeSoundCard.Map (SoundCard :: getUSB);
Dies entspricht der Verwendung der Kartenfunktion mit Stream. Durch die Verwendung von Stream muss eine Funktion als Parameter an die Kartenfunktion übergeben werden, und die übergebene Funktion wird auf jedes Element im Stream angewendet. Wenn Raum und Zeit streamen, passiert nichts.
Der in optionale enthaltene Wert wird durch die in übergebene Funktion konvertiert (hier ist eine Funktion, die USB von der Tonkarte erhält). Wenn das optionale Objekt Raumzeit ist, wird nichts passieren.
Anschließend kombinieren wir die Kartenmethode und die Filtermethode, um Soundkarten mit USB -Versionsnummer nicht 3.0 herauszufiltern.
MaybeSoundCard.map (Soundcard :: getUSB) .Filter (USB -> "3.0" .Equals (USB.Getversion ()) .ifpresent (() -> System.out.println ("OK")); Auf diese Weise sieht unser Code ein bisschen wie das aus, was wir am Anfang ohne Null -Erkennung gegeben haben.
Optionales Objekt mit der FlatMap -Funktion übergeben
Nun wurde ein Beispiel dafür eingeführt, wie der Code mithilfe optional neu gestaltet wird. Wie sollten wir den folgenden Code auf sichere Weise implementieren?
String Version = computer.getSoundCard (). GetUSB (). Getversion ();
Beachten Sie, dass der obige Code alle ein anderes Objekt aus einem Objekt extrahiert, das mit der Kartenfunktion implementiert werden kann. Im vorherigen Artikel haben wir ein Optional<Soundcard> -Objekt auf dem Computer eingerichtet, und Soundcard enthält ein Optional<USB> -Objekt, sodass wir den Code auf diese Weise neu ausführen können
String Version = computer.map (computer :: gotsoundcard) .MAP (SoundCard :: getUSB) .MAP (USB :: Getversion) .Orelse ("unbekannt"); Leider erstellt der obige Code Fehler. Warum? Die Computervariable ist vom Typ Optional<Computer> , daher hat sie kein Problem mit der Kartenfunktion. getSoundcard() -Methode gibt jedoch ein Optional<Soundcard> -Objekt zurück, das ein Objekt vom Typ Optional<Optional<Soundcard>> zurückgibt. Nachdem die zweite Kartenfunktion aufgerufen wurde, wird der Aufruf zur Funktion getUSB() illegal.
Die folgende Abbildung beschreibt dieses Szenario:
Die Quellcode -Implementierung der Kartenfunktion lautet wie folgt:
public <u> optional <u> map (Funktion <? Super t,? Erweitert u> mapper) {Objects.requirenonnull (Mapper); if (! isPresent ()) kehre leer zurück (); sonst {return optional.ofnulable (mapper.apply (Wert)); }} Es ist zu sehen, dass die Kartenfunktion Optional.ofNullable() erneut aufruft, was zur Rückgabe von Optional<Optional<Soundcard>> führt
Optional liefert die FlatMap-Funktion, mit der der Wert des optionalen Objekts (z. Die folgende Abbildung zeigt den Unterschied zwischen optionalen Objekten in der Konvertierung des Typs, indem Sie MAP und FlatMap aufrufen:
So können wir das schreiben:
String Version = computer.flatMap (computer :: gotsoundCard) .flatMap (SoundCard :: getUSB) .MAP (USB :: Getversion) .Orelse ("unbekannt"); Die erste FlatMap stellt sicher, dass die Rückgabe Optional<Soundcard> anstatt Optional<Optional<Soundcard>> , und die zweite Flatmap implementiert die gleiche Funktion, sodass die Rückgabe Optional<USB> ist. Beachten Sie, dass map() das dritte Mal aufgerufen wird, da getVersion() ein String -Objekt anstelle eines optionalen Objekts zurückgibt.
Wir schreiben schließlich den hässlichen Code von verschachtelten Nullprüfungen um, die wir gerade verwendet haben, was sehr lesbar ist, und vermeiden auch das Auftreten von Ausnahmen von Nullzeiger.
Zusammenfassen
In diesem Artikel übernehmen wir die neue Klasse java.util.Optional<T> von Java 8. Die ursprüngliche Absicht dieser Klasse besteht nicht darin, Nullreferenzen zu ersetzen, sondern Designer zu helfen, bessere APIs zu entwerfen. Lesen Sie einfach die Signatur der Funktion und wissen Sie, ob die Funktion einen Wert akzeptiert, der möglicherweise existiert oder nicht. Zusätzlich zwingt Sie optional, optional einzuschalten und dann zu verarbeiten, ob der Wert existiert, wodurch Ihr Code potenzielle Ausnahmen von Nullzeiger vermeidet.
Okay, das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Referenzwert für das Studium oder die Arbeit eines jeden hat. Wenn Sie Fragen haben, können Sie eine Nachricht zur Kommunikation überlassen. Vielen Dank für Ihre Unterstützung bei Wulin.com.