{"id":3958,"date":"2017-08-10T16:43:35","date_gmt":"2017-08-10T14:43:35","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/?p=3958"},"modified":"2017-08-10T16:43:35","modified_gmt":"2017-08-10T14:43:35","slug":"java-util-optional-ist-keine-nullnummer","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2017\/08\/java-util-optional-ist-keine-nullnummer\/","title":{"rendered":"java.util.Optional ist keine Nullnummer"},"content":{"rendered":"<p>Java hat eine besondere Referenz, die Entwicklern die Haare zu Berge stehen l\u00e4sst und die Grund f\u00fcr lange Debug-Stunden ist: die null-Referenz. Eigentlich sagt null nur aus: \u201enicht initialisiert\u201c. Doch was null so problematisch macht, ist die NullPointerException, die durch referenzierte null-Ausdr\u00fccke ausgel\u00f6st wird.<\/p>\n<p><strong>Beispiel:\u00a0<\/strong>Entwickler haben vergessen, das Attribut location mit einem Objekt zu initialisieren, sodass setLocation(\u2026) fehlschlagen wird:<\/p>\n<pre>class\u00a0Place\u00a0{\r\n \u00a0\u00a0private\u00a0Point2D\u00a0location;\r\n \u00a0\u00a0public\u00a0void\u00a0setLocation(\u00a0double\u00a0longitude,\u00a0double\u00a0latitude\u00a0)\u00a0{\r\n \u00a0\u00a0\u00a0\u00a0location.setLocation(\u00a0longitude,\u00a0latitude\u00a0);\u00a0\u00a0\/\/\u00a0BANG!\u00a0NullPointerException\r\n \u00a0\u00a0}\r\n }<\/pre>\n<h4>Einsatz von null<\/h4>\n<p>Fehler dieser Art sind durch Tests relativ leicht aufzusp\u00fcren. Aber hier liegt nicht das Problem. Das eigentliche Problem ist, dass Entwickler allzu gerne die typenlose null<a href=\"#_ftn1\" name=\"_ftnref1\">[1]<\/a> als magischen Sonderwert sehen, sodass sie neben \u201enicht initialisiert\u201c noch etwas anderes bedeutet:<\/p>\n<ul>\n<li>Erlaubt die API in Argumenten f\u00fcr Methoden\/Konstruktoren null, hei\u00dft das meistens \u201enutze einen Default-Wert\u201c oder \u201enichts gegeben, ignorieren\u201c.<\/li>\n<li>In R\u00fcckgaben von Methoden steht null oftmals f\u00fcr \u201enichts gemacht\u201c oder \u201ekeine R\u00fcckgabe\u201c. Im Gegensatz dazu kodieren andere Methoden wiederum mit der R\u00fcckgabe null, dass eine Operation erfolgreich durchlaufen wurde, und w\u00fcrden sonst zum Beispiel Fehlerobjekte zur\u00fcckgegeben.<a href=\"#_ftn2\" name=\"_ftnref2\">[2]<\/a><\/li>\n<\/ul>\n<p>Beispiel 1<\/p>\n<p>Die mit Javadoc dokumentiere Methode getTask(out, fileManager, diagnosticListener, options, classes, compilationUnits) in der Schnittstelle JavaCompiler ist so ein Beispiel:<\/p>\n<ul>\n<li>out: \u201ea writer for additional output from the compiler; use system.err <strong>if<\/strong> <strong>null\u201c<\/strong><\/li>\n<li>fileManager: \u201ea file manager; <strong>if<\/strong> <strong>null<\/strong> use the compiler\u2019s standard filemanager\u201c<\/li>\n<li>diagnosticListener: \u201ea diagnostic listener; <strong>if<\/strong> <strong>null<\/strong> use the compiler\u2019s default method for reporting diagnostics\u201c<\/li>\n<li>options: \u201ecompiler options, <strong>null<\/strong> means no options\u201c<\/li>\n<li>classes: \u201enames of classes to be processed by annotation processing, <strong>null<\/strong> means no class names\u201c<\/li>\n<li>compilationUnits: \u201ethe compilation units to compile, null means no compilation units\u201c<\/li>\n<\/ul>\n<p>Alle Argumente k\u00f6nnen null sein, getTask(null, null, null, null, null, null) ist ein korrekter Aufruf. Sch\u00f6n ist die API nicht, und besser w\u00e4re sie wohl mit einem Builder-Pattern gel\u00f6st.<\/p>\n<p>Beispiel 2<\/p>\n<p>Der BufferedReader erlaubt das zeilenweise Einlesen aus Datenquellen, und readLine() liefert null, wenn es keine Zeile mehr zu lesen gibt.<\/p>\n<p>Beispiel 3<\/p>\n<p>Viel Irritation gibt es mit der API vom Assoziativspeicher. Eine gew\u00f6hnliche HashMap kann als assoziierten Wert null bekommen, doch get(key) liefert auch dann null, wenn es keinen assoziierten Wert gibt. Das f\u00fchrt zu einer Mehrdeutigkeit, da die R\u00fcckgabe von get(\u2026) nicht verr\u00e4t, ob es eine Abbildung auf null gibt oder ob der Schl\u00fcssel nicht vorhanden ist.<\/p>\n<pre>Map&lt;Integer,String&gt;\u00a0map\u00a0=\u00a0new\u00a0HashMap&lt;&gt;();\r\n map.put(\u00a00,\u00a0null\u00a0);\r\n System.out.println(\u00a0map.containsKey(\u00a00\u00a0)\u00a0);\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/\u00a0true\r\n System.out.println(\u00a0map.containsValue(\u00a0null\u00a0)\u00a0);\u00a0\/\/\u00a0true\r\n System.out.println(\u00a0map.get(\u00a00\u00a0)\u00a0);\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/\u00a0null\r\n System.out.println(\u00a0map.get(\u00a01\u00a0)\u00a0);\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/\u00a0null<\/pre>\n<p>Kann die Map null-Werte enthalten, muss es immer ein Paar der Art if(map.containsKey(key)), gefolgt von map.get(key) geben. Am besten verzichten Entwickler auf null in Datenstrukturen.<\/p>\n<p>Da null so viele Einsatzf\u00e4lle hat und das Lesen der API-Dokumentation gerne \u00fcbersprungen wird, sollte es zu einigen null-Eins\u00e4tzen Alternativen geben. Manches Mal ist das einfach, etwa wenn die R\u00fcckgabe Sammlungen sind. Dann gibt es mit einer leeren Sammlung eine gute Alternative zu null. Das ist ein Spezialfall des so genannten Null-Object-Patterns.<\/p>\n<p>Fehler, die aufgrund einer NullPointerException entstehen, lie\u00dfen sich nat\u00fcrlich komplett vermeiden, wenn immer ordentlich auf null-Referenzen getestet w\u00fcrde. Aber gerade die null-Pr\u00fcfungen werden von Entwicklern gerne vergessen, da ihnen nicht bewusst ist oder sie nicht erwarten, dass eine R\u00fcckgabe null sein kann. Gew\u00fcnscht ist ein Programmkonstrukt, bei dem explizit wird, dass ein Wert nicht vorhanden sein kann, sodass nicht null diese Rolle \u00fcbernehmen muss. Wenn im Code lesbar ist, dass ein Wert optional ist, also vorhanden sein kann oder nicht, reduziert das Fehler.<\/p>\n<p>Geschichte<\/p>\n<p>Tony Hoare gilt als \u201eErfinder\u201c der null-Referenz. Heute bereut er es und nennt die Entscheidung \u201emy billion-dollar mistake\u201c.<a href=\"#_ftn3\" name=\"_ftnref3\">[3]<\/a><\/p>\n<h3>Optional-Typ<\/h3>\n<p>Die Java-Bibliothek bietet eine Art Container, der ein Element enthalten kann oder nicht. Wenn der Container ein Element enth\u00e4lt, ist es nie null. Dieser Container kann befragt werden, ob er ein Element enth\u00e4lt oder nicht. Eine null als Kennung ist somit \u00fcberfl\u00fcssig.<\/p>\n<p>Beispiel<\/p>\n<pre>Optional&lt;String&gt;\u00a0opt1\u00a0=\u00a0Optional.of(\u00a0\"Aitazaz\u00a0Hassan\u00a0Bangash\"\u00a0);\r\n System.out.println(\u00a0opt1.isPresent()\u00a0);\u00a0\u00a0\u00a0\/\/\u00a0true\r\n System.out.println(\u00a0opt1.get()\u00a0);\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/\u00a0Aitazaz\u00a0Hassan\u00a0Bangash\r\n Optional&lt;String&gt;\u00a0opt2\u00a0=\u00a0Optional.empty();\r\n System.out.println(\u00a0opt2.isPresent()\u00a0);\u00a0\u00a0\u00a0\/\/\u00a0false\r\n \/\/\u00a0opt2.get()\u00a0-&gt;\u00a0java.util.NoSuchElementException:\u00a0No\u00a0value\u00a0present\r\n Optional&lt;String&gt;\u00a0opt3\u00a0=\u00a0Optional.ofNullable(\u00a0\"Malala\"\u00a0);\r\n System.out.println(\u00a0opt3.isPresent()\u00a0);\u00a0\u00a0\u00a0\/\/\u00a0true\r\n System.out.println(\u00a0opt3.get()\u00a0);\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/\u00a0Malala\r\n Optional&lt;String&gt;\u00a0opt4\u00a0=\u00a0Optional.ofNullable(\u00a0null\u00a0);\r\n System.out.println(\u00a0opt4.isPresent()\u00a0);\u00a0\u00a0\u00a0\/\/\u00a0false\r\n \/\/\u00a0opt4.get()\u00a0-&gt;\u00a0java.util.NoSuchElementException:\u00a0No\u00a0value\u00a0present<\/pre>\n<p>final\u00a0class\u00a0java.util.Optional&lt;T&gt;<\/p>\n<ul>\n<li>static&lt;T&gt;Optional&lt;T&gt;empty()<br \/>\nLiefert ein leeres Optional-Objekt.<\/li>\n<li>booleanisPresent()<br \/>\nLiefert wahr, wenn dieses Optional einen Wert hat, sonst ist wie im Fall von empty() die R\u00fcckgabe false.<\/li>\n<li>static&lt;T&gt;Optional&lt;T&gt;of(Tvalue)<br \/>\nBaut ein neues Optional mit einem Wert auf, der nicht null sein darf; andernfalls gibt es eine NullPointerException. null in das Optional hineinzubekommen, geht also nicht.<\/li>\n<li>static&lt;T&gt;Optional&lt;T&gt;ofNullable(Tvalue)<br \/>\nLiefert ein Optional mit dem Wert, wenn dieser ungleich null ist, bei null ist die R\u00fcckgabe ein empty().<\/li>\n<li>Tget()<br \/>\nLiefert den Wert. Enth\u00e4lt das Optional keinen Wert, weil kein Wert isPresent() ist, folgt eine NoSuchElementException.<\/li>\n<li>TorElse(Tother)<br \/>\nIst ein Wert isPresent(), liefere den Wert. Ist das Optional leer, liefere other.<\/li>\n<li>Stream&lt;T&gt; stream()<br \/>\nKonvertiere das Optional in den Datentyp Stream. Neu in Java 9.<\/li>\n<\/ul>\n<p>Des Weiteren \u00fcberschreibt Optional die Methoden equals(\u2026), toString() und hashCode() \u2013 0, wenn kein Wert gegeben ist, sonst Hashcode vom Element \u2013 und ein paar weitere Methoden, die wir uns sp\u00e4ter anschauen.<\/p>\n<p><strong>Hinweis:\u00a0<\/strong>Intern null zu verwenden hat zum Beispiel den Vorteil, dass die Objekte serialisiert werden k\u00f6nnen. Optional implementiert Serializable nicht, daher sind Optional-Attribute nicht serialisierbar, k\u00f6nnen also etwa nicht im Fall von Remote-Aufrufen mit RMI \u00fcbertragen werden. Auch die Abbildung auf XML oder auf Datenbanken ist umst\u00e4ndlicher, wenn nicht JavaBean-Properties herangezogen werden, sondern die internen Attribute.<\/p>\n<h4>Ehepartner oder nicht?<\/h4>\n<p>Optional wird also dazu verwendet, im Code explizit auszudr\u00fccken, ob ein Wert vorhanden ist oder nicht. Das gilt auf beiden Seiten: Der Erzeuger muss explizit ofXXX(\u2026) aufrufen und der Nutzer explizit isPresent() oder get(). Beide Seiten sind sich bewusst, dass sie es mit einem Wert zu tun haben, der optional ist, also existieren kann oder nicht. Wir wollen das in einem Beispiel nutzen, und zwar f\u00fcr eine Person, die einen Ehepartner haben kann:<\/p>\n<pre>public\u00a0class\u00a0Person\u00a0{\r\n \u00a0\u00a0private\u00a0Person\u00a0spouse;\r\n \u00a0\u00a0public\u00a0void\u00a0setSpouse(\u00a0Person\u00a0spouse\u00a0)\u00a0{\r\n \u00a0\u00a0\u00a0\u00a0this.spouse\u00a0=\u00a0Objects.requireNonNull(\u00a0spouse\u00a0);\r\n \u00a0\u00a0}\r\n \u00a0\u00a0public\u00a0void\u00a0removeSpouse()\u00a0{\r\n \u00a0\u00a0\u00a0\u00a0spouse\u00a0=\u00a0null;\r\n \u00a0\u00a0}\r\n \u00a0\u00a0public\u00a0Optional&lt;Person&gt;\u00a0getSpouse()\u00a0{\r\n \u00a0\u00a0\u00a0\u00a0return\u00a0Optional.ofNullable(\u00a0spouse\u00a0);\r\n \u00a0\u00a0}\r\n }<\/pre>\n<p>In diesem Beispiel ist null f\u00fcr die interne Referenz auf den Partner m\u00f6glich; diese Kodierung soll aber nicht nach au\u00dfen gelangen. Daher liefert getSpouse() nicht direkt die Referenz, sondern es kommt Optional zum Einsatz und dr\u00fcckt aus, ob eine Person einen Ehepartner hat oder nicht. Auch bei setSpouse(\u2026) akzeptieren wir kein null, denn null-Argumente sollten so weit wie m\u00f6glich vermieden werden. Ein Optional ist hier nicht angemessen, weil es ein Fehler ist, null zu \u00fcbergeben. Zus\u00e4tzlich sollte nat\u00fcrlich die Javadoc an setSpouse(\u2026) dokumentieren, dass ein null-Argument zu einer NullPointerException f\u00fchrt. Daher passt Optional als Parametertyp nicht.<\/p>\n<pre>Person\u00a0heinz\u00a0=\u00a0new\u00a0Person();\r\n System.out.println(\u00a0heinz.getSpouse().isPresent()\u00a0);\u00a0\/\/\u00a0false\r\n Person\u00a0eva\u00a0=\u00a0new\u00a0Person();\r\n heinz.setSpouse(\u00a0eva\u00a0);\r\n System.out.println(\u00a0heinz.getSpouse().isPresent()\u00a0);\u00a0\/\/\u00a0true\r\n System.out.println(\u00a0heinz.getSpouse().get()\u00a0);\u00a0\/\/\u00a0com\/\u2026\/Person\r\n heinz.removeSpouse();\r\n System.out.println(\u00a0heinz.getSpouse().isPresent()\u00a0);\u00a0\/\/\u00a0false<\/pre>\n<h3>Primitive optionale Typen<\/h3>\n<p>W\u00e4hrend Referenzen null sein k\u00f6nnen und auf diese Weise das Nichtvorhandensein anzeigen, ist das bei primitiven Datentypen nicht so einfach. Wenn eine Methode ein boolean zur\u00fcckgibt, bleibt neben true und false nicht viel \u00fcbrig, und ein \u201enicht zugewiesen\u201c wird dann doch gerne wieder \u00fcber einen Boolean verpackt und auf null getestet. Gerade bei Ganzzahlen gibt es immer wieder R\u00fcckgaben wie \u20131.<a href=\"#_ftn4\" name=\"_ftnref4\">[4]<\/a> Das ist bei den folgenden Beispielen der Fall:<\/p>\n<ul>\n<li>Wenn bei InputStreams read(\u2026) keine Eingaben mehr kommen, wird -1 zur\u00fcckgegeben.<\/li>\n<li>indexOf(Object) von List liefert -1, wenn das gesuchte Objekt nicht in der Liste ist und folglich auch keine Position vorhanden ist.<\/li>\n<li>Bei einer unbekannten Bytel\u00e4nge einer MIDI-Datei (Typ MidiFileFormat) hat getByteLength() als R\u00fcckgabe -1.<\/li>\n<\/ul>\n<p>Diese magischen Werte sollten vermieden werden, und daher kann auch der optionale Typ wieder erscheinen.<\/p>\n<p>Als generischer Typ kann Optional beliebige Typen kapseln, und primitive Werte k\u00f6nnten in Wrapper verpackt werden. Allerdings bietet Java f\u00fcr drei primitive Typen spezielle Optional-Typen an: OptionalInt, OptionalLong, OptionalDouble:<\/p>\n<table>\n<tbody>\n<tr>\n<td>Optional&lt;T&gt;<\/td>\n<td>OptionalInt<\/td>\n<td>OptionalLong<\/td>\n<td>OptionalDouble<\/td>\n<\/tr>\n<tr>\n<td>static &lt;T&gt;<br \/>\nOptional&lt;T&gt;<br \/>\nempty()<\/td>\n<td>static<br \/>\nOptionalInt<br \/>\nempty()<\/td>\n<td>static<br \/>\nOptionalLong<br \/>\nempty()<\/td>\n<td>static<br \/>\nOptional-Double<br \/>\nempty()<\/td>\n<\/tr>\n<tr>\n<td>T get()<\/td>\n<td>int getAsInt()<\/td>\n<td>long getAsLong()<\/td>\n<td>double getAsDouble()<\/td>\n<\/tr>\n<tr>\n<td colspan=\"4\">boolean isPresent()<\/td>\n<\/tr>\n<tr>\n<td>static &lt;T&gt;<br \/>\nOptional&lt;T&gt;<br \/>\nof(T value)<\/td>\n<td>static OptionalInt<br \/>\nof(int value)<\/td>\n<td>static OptionalLong<br \/>\nof(long value)<\/td>\n<td>static OptionalDouble<br \/>\nof(double value)<\/td>\n<\/tr>\n<tr>\n<td>static &lt;T&gt;<br \/>\nOptional&lt;T&gt;<br \/>\nofNullable(T<br \/>\nvalue)<\/td>\n<td colspan=\"3\">nicht \u00fcbertragbar<\/td>\n<\/tr>\n<tr>\n<td>T orElse(T other)<\/td>\n<td>int orElse(int other)<\/td>\n<td>long orElse(long other)<\/td>\n<td>double orElse(<br \/>\ndouble other)<\/td>\n<\/tr>\n<tr>\n<td colspan=\"4\">boolean equals(Object obj)<\/td>\n<\/tr>\n<tr>\n<td colspan=\"4\">int hashCode()<\/td>\n<\/tr>\n<tr>\n<td colspan=\"4\">String toString()<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Tabelle: Methodenvergleich zwischen den vier OptionalXXX-Klassen<\/p>\n<p>Die Optional-Methode ofNullable(\u2026) f\u00e4llt in den primitiven Optional-Klassen nat\u00fcrlich raus. Die optionalen Typen f\u00fcr die drei primitiven Typen haben insgesamt weniger Methoden, und die obere Tabelle ist nicht ganz vollst\u00e4ndig. Wir kommen im Rahmen der funktionalen Programmierung in Java noch auf die verbleibenden Methoden wie isPresent(\u2026) zur\u00fcck.<\/p>\n<p><strong>Best Practice:\u00a0<\/strong>OptionalXXX-Typen eignen sich hervorragend als R\u00fcckgabetyp, sind als Parametertyp denkbar, doch wenig attraktiv f\u00fcr interne Attribute. Intern ist null eine akzeptable Wahl, der \u201eTyp\u201c ist schnell und speicherschonend.<\/p>\n<h3>Erstmal funktional mit Optional<\/h3>\n<p>Neben den vorgestellten Methoden wie ofXXX(\u2026) und isPresent() gibt es weitere, die auf funktionale Schnittstellen zur\u00fcckgreifen:<\/p>\n<p>final\u00a0class\u00a0java.lang.Optional&lt;T&gt;<\/p>\n<ul>\n<li>voidifPresent(Consumer&lt;?superT&gt;consumer)<br \/>\nRepr\u00e4sentiert das Optional einen Wert, rufe den Consumer mit diesem Wert auf, andernfalls mache nichts.<\/li>\n<li>void ifPresentOrElse(Consumer&lt;? super T&gt; action, Runnable emptyAction)<br \/>\nRepr\u00e4sentiert das Optional einen Wert, rufe den Consumer mit diesem Wert auf, andernfalls f\u00fchre emptyAction Das Runnable muss hier als Typ aus java.lang herhalten, weil es im java.util.function-Paket keine Schnittstelle gibt, die keine Parameter hat und auch keine R\u00fcckgabe liefert. Neu in Java 9.<\/li>\n<li>Optional&lt;T&gt;filter(Predicate&lt;?superT&gt;predicate)<br \/>\nEnth\u00e4lt das Optional einen Wert und ist das Pr\u00e4dikat predicate auf dem Wert wahr, ist die R\u00fcckgabe das eigene Optional (also this), sonst ist die R\u00fcckgabe empty().<\/li>\n<li>&lt;U&gt;Optional&lt;U&gt;map(Function&lt;?superT,?extendsU&gt;mapper)<br \/>\nRepr\u00e4sentiert das Optional einen Wert, dann wende die Funktion an und verpacke das Ergebnis (wenn es ungleich null ist) wieder in ein Optional. Ist das Optional ohne Wert, dann ist die R\u00fcckgabe empty(), genauso, wenn die Funktion null liefert.<\/li>\n<li>&lt;U&gt;Optional&lt;U&gt;flatMap(Function&lt;?superT,Optional&lt;U&gt;&gt;mapper)<br \/>\nWie map(\u2026), nur dass die Funktion ein Optional statt eines direkten Werts gibt. Liefert die Funktion mapper ein leeres Optional, so ist das Ergebnis von flatMap(\u2026) auch empty().<\/li>\n<li>Optional&lt;T&gt; or(Supplier&lt;? extends Optional&lt;? extends T&gt;&gt; supplier)<br \/>\nRepr\u00e4sentiert das Optional einen Wert, so liefere ihn. Ist das Optional leer, beziehe den Wert aus dem anderen Optional. Neu in Java 9.<\/li>\n<li>TorElseGet(Supplier&lt;?extendsT&gt;other)<br \/>\nRepr\u00e4sentiert das Optional einen Wert, so liefere ihn; ist das Optional leer, so beziehe den Alternativwert aus dem Supplier.<\/li>\n<li>&lt;XextendsThrowable&gt;TorElseThrow(Supplier&lt;?extendsX&gt;exceptionSupplier)<br \/>\nRepr\u00e4sentiert das Optional einen Wert, so liefere ihn, andernfalls hole mit Supplier das Ausnahme-Objekt, und l\u00f6se es aus.<\/li>\n<\/ul>\n<p><strong>Beispiel:\u00a0<\/strong>Wenn das Optional keinen Wert hat, soll eine NullPointerException statt der NoSuchElementException ausgel\u00f6st werden.<\/p>\n<p>String\u00a0s\u00a0=\u00a0optionalString.orElseThrow(\u00a0NullPointerException::new\u00a0);<\/p>\n<h4>Beispiel f\u00fcr NullPointerException-sichere Kaskadierung von Aufrufen mit Optional<\/h4>\n<p>Die beiden XXXmap(\u2026)-Methoden sind besonders interessant und erm\u00f6glichen einen ganz neuen Programmierstil. Warum, soll ein Beispiel zeigen.<\/p>\n<p>Der folgende Zweizeiler gibt auf meinem System \u201eMICROSOFT KERNELDEBUGGER-NETZWERKADAPTER\u201c aus:<\/p>\n<pre>String\u00a0s\u00a0=\u00a0NetworkInterface.getByIndex(\u00a02\u00a0).getDisplayName().toUpperCase();\r\nSystem.out.println(\u00a0s\u00a0);<\/pre>\n<p>Allerdings ist der Programmcode alles andere als gut, denn NetworkInterface.getByIndex(int) kann null zur\u00fcckgeben und getDisplayName() auch. Um ohne eine NullPointerException um die Klippen zu schiffen, m\u00fcssen wir schreiben:<\/p>\n<pre>NetworkInterface\u00a0networkInterface\u00a0=\u00a0NetworkInterface.getByIndex(\u00a02\u00a0);\r\n if\u00a0(\u00a0networkInterface\u00a0!=\u00a0null\u00a0)\u00a0{\r\n \u00a0\u00a0String\u00a0displayName\u00a0=\u00a0networkInterface.getDisplayName();\r\n \u00a0\u00a0if\u00a0(\u00a0displayName\u00a0!=\u00a0null\u00a0)\r\n \u00a0\u00a0\u00a0\u00a0System.out.println(\u00a0displayName.toUpperCase()\u00a0);\r\n }<\/pre>\n<p>Von der Eleganz des Zweizeilers ist nicht mehr viel geblieben. Integrieren wir Optional (was ja eigentlich ein toller R\u00fcckgabetyp f\u00fcr getByIndex() und getDisplayName() w\u00e4re):<\/p>\n<pre>Optional&lt;NetworkInterface&gt;\u00a0networkInterface\u00a0=\u00a0Optional.ofNullable(\u00a0NetworkInterface.getByIndex(\u00a02\u00a0)\u00a0);\r\n if\u00a0(\u00a0networkInterface.isPresent()\u00a0)\u00a0{\r\n \u00a0\u00a0Optional&lt;String&gt;\u00a0name\u00a0=\u00a0Optional.ofNullable(\u00a0networkInterface.get().getDisplayName()\u00a0);\r\n \u00a0\u00a0if\u00a0(\u00a0name.isPresent()\u00a0)\r\n \u00a0\u00a0\u00a0\u00a0System.out.println(\u00a0name.get().toUpperCase()\u00a0);\r\n }<\/pre>\n<p>Mit Optional wird es nicht sofort besser, doch statt if k\u00f6nnen wir einen Lambda-Ausdruck nehmen und bei ifPresent(\u2026) einsetzen:<\/p>\n<pre>Optional&lt;NetworkInterface&gt;\u00a0networkInterface\u00a0=\u00a0Optional.ofNullable(\u00a0NetworkInterface.getByIndex(\u00a02\u00a0)\u00a0);\r\n networkInterface.ifPresent(\u00a0ni\u00a0-&gt;\u00a0{\r\n \u00a0\u00a0Optional&lt;String&gt;\u00a0displayName\u00a0=\u00a0Optional.ofNullable(\u00a0ni.getDisplayName()\u00a0);\r\n \u00a0\u00a0displayName.ifPresent(\u00a0name\u00a0-&gt;\u00a0{\r\n \u00a0\u00a0\u00a0\u00a0System.out.println(\u00a0name.toUpperCase()\u00a0);\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\r\n \u00a0\u00a0}\u00a0);\r\n }\u00a0);<\/pre>\n<p>Wenn wir die lokalen Variablen networkInterface und displayName entfernen, landen wir bei:<\/p>\n<pre>Optional.ofNullable(\u00a0NetworkInterface.getByIndex(\u00a02\u00a0)\u00a0).ifPresent(\u00a0ni\u00a0-&gt;\u00a0{\r\n \u00a0\u00a0Optional.ofNullable(\u00a0ni.getDisplayName()\u00a0).ifPresent(\u00a0name\u00a0-&gt;\u00a0{\r\n \u00a0\u00a0\u00a0\u00a0System.out.println(\u00a0name.toUpperCase()\u00a0);\r\n \u00a0\u00a0}\u00a0);\r\n }\u00a0);<\/pre>\n<p>Von der Struktur her ist das mit der if-Abfrage identisch und \u00fcber die Einr\u00fcckungen auch zu erkennen. Fallunterscheidungen mit Optional und ifPresent(\u2026) umzuschreiben bringt keinen Vorteil.<\/p>\n<p>In Fallunterscheidungen zu denken hilft hier nicht weiter. Was wir uns bei NetworkInterface.getByIndex( 2 ).getDisplayName().toUpperCase() vor Augen halten m\u00fcssen, ist eine Kette von Abbildungen. NetworkInterface.getByIndex(int) bildet auf NetworkInterface ab, getDisplayName() von NetworkInterface bildet auf String ab, und toUpperCase() bildet von einem String auf einen anderen String ab. Wir verketten drei Abbildungen und m\u00fcssten ausdr\u00fccken k\u00f6nnen: Wenn eine Abbildung fehlschl\u00e4gt, dann h\u00f6re mit der Abbildung auf. Und genau hier kommen Optional und map(\u2026) ins Spiel. In Code:<\/p>\n<pre>Optional&lt;String&gt;\u00a0s\u00a0=\u00a0Optional.ofNullable(\u00a0NetworkInterface.getByIndex(\u00a02\u00a0)\u00a0)\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.map(\u00a0ni\u00a0-&gt;\u00a0ni.getDisplayName()\u00a0)\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.map(\u00a0name\u00a0-&gt;\u00a0name.toUpperCase()\u00a0);\r\n s.ifPresent(\u00a0System.out::println\u00a0);<\/pre>\n<p>Die Klasse Optional hilft uns bei zwei Dingen: Erstens wird map(\u2026) beim Empfangen einer null-Referenz auf ein Optional.empty() abbilden, und zweitens ist das Verketten von leeren Optionals kein Problem, es passiert einfach nichts \u2013 Optional.empty().map(\u2026) f\u00fchrt nichts aus, und die R\u00fcckgabe ist einfach nur ein leeres Optional. Am Ende der Kette steht nicht mehr String (wie am Anfang des Beispiels), sondern Optional&lt;String&gt;.<\/p>\n<p>Umgeschrieben mit Methodenreferenzen und weiter verk\u00fcrzt ist der Code sehr gut lesbar und Null-Pointer-Exception-sicher:<\/p>\n<p>Optional.ofNullable(\u00a0NetworkInterface.getByIndex(\u00a02\u00a0)\u00a0)<br \/>\n.map(\u00a0NetworkInterface::getDisplayName\u00a0)<br \/>\n.map(\u00a0String::toUpperCase\u00a0)<br \/>\n.ifPresent(\u00a0System.out::println\u00a0);<\/p>\n<p>Die Logik kommt ohne externe Fallunterscheidungen aus und arbeitet nur mit optionalen Abbildungen. Das ist ein sch\u00f6nes Beispiel f\u00fcr funktionale Programmierung.<\/p>\n<h4>Primitiv-Optionales mit<\/h4>\n<p>Die eigentliche Optional-Klasse ist generisch und kapselt jeden Referenztyp. Auch f\u00fcr die primitiven Typen int, long und double gibt es in drei speziellen Klassen OptionalInt, OptionalLong, OptionalDouble Methoden zur funktionalen Programmierung. Stellen wir die Methoden der vier OptionalXXX-Klassen gegen\u00fcber:<\/p>\n<table>\n<tbody>\n<tr>\n<td>Optional&lt;T&gt;<\/td>\n<td>OptionalInt<\/td>\n<td>OptionalLong<\/td>\n<td>OptionalDouble<\/td>\n<\/tr>\n<tr>\n<td>static &lt;T&gt;<br \/>\nOptional&lt;T&gt;<br \/>\nempty()<\/td>\n<td>static<br \/>\nOptionalInt<br \/>\nempty()<\/td>\n<td>static<br \/>\nOptionalLong<br \/>\nempty()<\/td>\n<td>static<br \/>\nOptionalDouble<br \/>\nempty()<\/td>\n<\/tr>\n<tr>\n<td>T get()<\/td>\n<td>int getAsInt()<\/td>\n<td>long getAsLong()<\/td>\n<td>double<br \/>\ngetAsDouble()<\/td>\n<\/tr>\n<tr>\n<td colspan=\"4\">boolean isPresent()<\/td>\n<\/tr>\n<tr>\n<td>static &lt;T&gt;<br \/>\nOptional&lt;T&gt; of(T value)<\/td>\n<td>static OptionalInt<br \/>\nof(<strong>int<\/strong> value)<\/td>\n<td>static OptionalLong<br \/>\nof(<strong>long<\/strong> value)<\/td>\n<td>static Optional<br \/>\nDouble<br \/>\nof(<strong>double<\/strong> value)<\/td>\n<\/tr>\n<tr>\n<td>static &lt;T&gt;<br \/>\nOptional&lt;T&gt;<br \/>\nofNullable(T value)<\/td>\n<td colspan=\"3\">nicht \u00fcbertragbar<\/td>\n<\/tr>\n<tr>\n<td>T orElse(T other)<\/td>\n<td><strong>int<\/strong> orElse(<strong>int<\/strong> other)<\/td>\n<td><strong>long<\/strong> orElse(<strong>long<\/strong> other)<\/td>\n<td><strong>double<\/strong> orElse(<strong>double<\/strong> other)<\/td>\n<\/tr>\n<tr>\n<td>Stream&lt;T&gt; stream()<\/td>\n<td><strong>Int<\/strong>Stream stream()<\/td>\n<td><strong>Long<\/strong>Stream stream()<\/td>\n<td><strong>Double<\/strong>Stream stream()<\/td>\n<\/tr>\n<tr>\n<td colspan=\"4\">boolean equals(Object obj)<\/td>\n<\/tr>\n<tr>\n<td colspan=\"4\">int hashCode()<\/td>\n<\/tr>\n<tr>\n<td colspan=\"4\">String toString()<\/td>\n<\/tr>\n<tr>\n<td>void ifPresent(<br \/>\nConsumer&lt;? super T&gt; consumer)<\/td>\n<td>void ifPresent(<br \/>\nIntConsumer<br \/>\nconsumer)<\/td>\n<td>void ifPresent(<br \/>\nLongConsumer<br \/>\nconsumer)<\/td>\n<td>void ifPresent(<br \/>\nDoubleConsumer<br \/>\nconsumer)<\/td>\n<\/tr>\n<tr>\n<td>void ifPresentOrElse( Consumer&lt;? super T&gt; action, Runnable emptyAction)<\/td>\n<td>void ifPresentOrElse( <strong>IntConsumer<\/strong> action, Runnable emptyAction)<\/td>\n<td>void ifPresentOrElse( <strong>LongConsumer<\/strong> action, Runnable emptyAction)<\/td>\n<td>void ifPresentOrElse( <strong>DoubleConsumer<\/strong> action, Runnable emptyAction)<\/td>\n<\/tr>\n<tr>\n<td>T orElseGet( Supplier&lt;? extends T&gt; other)<\/td>\n<td>int orElseGet( IntSupplier other)<\/td>\n<td>long orElseGet( LongSupplier other)<\/td>\n<td>double orElseGet( DoubleSupplier other)<\/td>\n<\/tr>\n<tr>\n<td>&lt;X extends Throwable&gt; T orElseThrow( Supplier&lt;? extends X&gt; exceptionSupplier)<\/td>\n<td>&lt;X extends Throwable&gt; int orElseThrow( Supplier&lt;? extends X&gt; exceptionSupplier)<\/td>\n<td>&lt;X extends Throwable&gt; long orElseThrow( Supplier&lt;? extends X&gt; exceptionSupplier)<\/td>\n<td>&lt;X extends Throwable&gt; double<br \/>\norElseThrow( Supplier&lt;? extends X&gt;<br \/>\nexceptionSupplier)<\/td>\n<\/tr>\n<tr>\n<td>Optional&lt;T&gt; filter(Predicate&lt;? super T&gt; predicate)<\/td>\n<td colspan=\"3\" rowspan=\"3\">nicht vorhanden<\/td>\n<\/tr>\n<tr>\n<td>&lt;U&gt; Optional&lt;U&gt;<br \/>\nflatMap(Function<br \/>\n&lt;? super T,Optional&lt;U&gt;&gt; mapper)<\/td>\n<\/tr>\n<tr>\n<td>&lt;U&gt; Optional&lt;U&gt; map(Function&lt;? super T,? extends U&gt; mapper)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Tabelle 1.13: Vergleich von Optional mit den primitiven OptionalXXX-Klassen<\/p>\n<p><a href=\"#_ftnref1\" name=\"_ftn1\"><sup>[1]<\/sup><\/a> \u00a0\u00a0\u00a0\u00a0 null instanceof Typ ist immer false .<\/p>\n<p><a href=\"#_ftnref2\" name=\"_ftn2\"><sup>[2]<\/sup><\/a> Zum Gl\u00fcck wird null selten als Fehler-Identifikator genutzt, die Zeiten sind vorbei. Hier sind Ausnahmen die bessere Wahl, denn Fehler sind Ausnahmen im Programm.<\/p>\n<p><a href=\"#_ftnref3\" name=\"_ftn3\"><sup>[3]<\/sup><\/a> \u00a0\u00a0\u00a0\u00a0 Er sagt dazu: \u201eIt was the invention of the\u00a0null\u00a0reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn\u2019t resist the temptation to put in a\u00a0null\u00a0reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.\u201c Unter http:\/\/www.infoq.com\/presentations\/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare gibt es ein Video mit ihm und Erkl\u00e4rungen.<\/p>\n<p><a href=\"#_ftnref4\" name=\"_ftn4\"><sup>[4]<\/sup><\/a> Unter http:\/\/docs.oracle.com\/javase\/8\/docs\/api\/constant-values.html lassen sich alle Konstantendeklarationen einsehen.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Java hat eine besondere Referenz, die Entwicklern die Haare zu Berge stehen l\u00e4sst und die Grund f\u00fcr lange Debug-Stunden ist: die null-Referenz. Eigentlich sagt null nur aus: \u201enicht initialisiert\u201c. Doch was null so problematisch macht, ist die NullPointerException, die durch referenzierte null-Ausdr\u00fccke ausgel\u00f6st wird. Beispiel:\u00a0Entwickler haben vergessen, das Attribut location mit einem Objekt zu initialisieren, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","_links_to":"","_links_to_target":""},"categories":[66],"tags":[],"class_list":["post-3958","post","type-post","status-publish","format-standard","hentry","category-java-8"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/3958","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/comments?post=3958"}],"version-history":[{"count":1,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/3958\/revisions"}],"predecessor-version":[{"id":3959,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/3958\/revisions\/3959"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=3958"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=3958"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=3958"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}