Angelika Langer - Training & Consulting
HOME | COURSES | TALKS | ARTICLES | GENERICS | LAMBDAS | IOSTREAMS | ABOUT | CONTACT | Twitter | Lanyrd | Linkedin
 
HOME 

  OVERVIEW

  BY TOPIC
    JAVA
    C++

  BY COLUMN
    EFFECTIVE JAVA
    EFFECTIVE STDLIB

  BY MAGAZINE
    JAVA MAGAZIN
    JAVA SPEKTRUM
    JAVA WORLD
    JAVA SOLUTIONS
    JAVA PRO
    C++ REPORT
    CUJ
    OTHER
 

GENERICS 
LAMBDAS 
IOSTREAMS 
ABOUT 
CONTACT 
Effective Java

Effective Java
Java 8
Optional<T>
 

Java Magazin, Mai 2015
Klaus Kreft & Angelika Langer

Dies ist die Überarbeitung eines Manuskripts für einen Artikel, der im Rahmen einer Kolumne mit dem Titel "Effective Java" im Java Magazin erschienen ist.  Die übrigen Artikel dieser Serie sind ebenfalls verfügbar ( click here ).

 
 

In unserer Reihe zum Thema Java 8 Neuerungen wollen wir uns diesmal die Klasse java.util.Optional<T> ansehen.  Wir hatten dieses Thema bereits in unserem Artikel über Kollektoren (/ JAV8-6 /) kurz angeschnitten.  Diesmal wollen wir ins Detail gehen.

Einleitung

Optional ist eine Abstraktion, die in Java 8 zum JDK hinzugekommen ist und von einigen Stream-Operationen als Returntyp verwendet wird.   Ein  Optional kann einen Wert enthalten oder aber leer sein, daher der Name: der Wert ist optional .  Da die Entwicklung der  Optional -Klasse von kontroversen Diskussionen begleitet wurde, sowohl im OpenJDK Forum (/OJLD/) wie auch außerhalb (/JPNG/), wollen wir sie näher anschauen. 

 
 

Genau genommen ist nicht nur die Klasse  Optional<T> zum JDK dazugekommen, sondern auch ihre primitiven Geschwisterklassen  OptionalInt OptionalLong und  OptionalDouble .  Wir werden uns in dem Artikel aber auf  Optional<T> konzentrieren, da die anderen Klassen nur Varianten für die primitiven Typen  int long und  double sind.  Alles Gesagt gilt so oder so ähnlich auch für die primitiven  Optional -Klassen.  Wo es Abweichungen gibt, werden wir diese erwähnen. 
 
 

Beginnen wir damit, wie  Optional im JDK benutzt wird.

Optional und Streams

Optional wird im JDK API von einigen wenigen Stream-Operationen als Returntyp genutzt.  Ein Beispiel dafür ist die Stream-Operation:

 
 

Optional<T> reduce(BinaryOperator<T> accumulator)
 
 

Warum gibt  reduce() hier ein  Optional<T> zurück?  Zumal es auch eine überladen Version von  reduce() gibt, die kein  Optional<T> sondern ein  T zurück gibt:
 
 

T reduce(T identity, BinaryOperator<T> accumulator)
 
 

Was macht den Unterschied?  Schauen wir uns das an einem Beispiel an, zuerst die Version, die  T zurückgibt:

int sum = Stream.of(-2, -1, 0, 1, 2)

                 .reduce(0, (i, j) -> i + j);

System.out.println("sum is: " + sum);


 
 

Diese Version funktioniert so, dass sie als ersten Parameter einen Anfangswert und als zweiten Parameter die reduce-Funktionalität übergeben bekommt.  Die Javadoc fordert, dass der Anfangswert bezüglich der reduce-Funktionalität das neutrale Element sein muss.  Das ist bei uns der Fall:  der Anfangswert ist 0, die reduce-Funktionalität ist die Integer-Addition und offensichtlich liefert die Addition mit 0 immer den Ausgangswert.  Lässt man das Beispiel ablaufen, ist seine Ausgabe wie folgt:

sum is: 0

Das ist nicht besonders überraschend. Wenn wir uns die Stream-Elemente ansehen, so können wir feststellen, dass sich diese in der Tat zu 0 addieren.  Falls man die Stream-Elemente aber nicht kennt, ist das Ergebnis 0 nicht so einfach zu interpretieren.  Es könnte ja so sein, dass der Stream leer ist und wir einfach nur den Anfangswert 0 wieder zurückbekommen, ohne dass es überhaupt zur Addition kommt.  Dies ist zum Beispiel hier der Fall:

int sum = Stream.of(-2, -1, 0, 1, 2)

                 .filter(i -> i > 10)

                 .reduce(0, (i, j) -> i + j);

System.out.println("sum is: " + sum);

Durch den Filter kommen nur die  Integer -Werte, die größer als 10 sind.  Das ist in unserem Fall keines der Stream-Elemente.  Trotzdem ist die Ausgabe die gleiche wie oben.
 
 

Um diese Situation auflösen zu können und entscheiden zu können, ob die Summe 0 ist oder ob nur der Anfangswert wieder zurückgegeben wurde, kann man die reduce-Operation nutzen, die keinen Anfangswert hat, dafür aber  Optional<T> zurückgibt.
 
 

Optional<Integer> opt = Stream.of(-2, -1, 0, 1, 2)

                              .reduce((i, j) -> i + j);
 
 

if (opt.isPresent())                                   // Zeile 4

   System.out.println("sum is: " + opt.get());

else

   System.out.println("stream was empty");
 
 

Um festzustellen, ob das  Optional überhaupt einen Wert enthält, nutzen wir die Methode  isPresent() (Zeile 4). Und um auf den Wert zuzugreifen, nutzen wir die Methode  get() (darauf folgende Zeile). 
 
 

Die Ausgabe dieses Programms ist natürlich wieder die gleiche wie bisher.  Wenn wir aber nun wieder die filter-Operation vor der reduce-Operation einbauen, ist die Ausgabe anders:

stream was empty

Damit haben wir unser Ziel erreicht und können die beiden Situationen "Summe der Elemente gleich 0" oder "leerer Stream" voneinander unterscheiden.
 
 

Bei allen anderen Stream-Operationen, die  Optional<T> als Returnwert haben, ist die Situation ähnlich.  Immer geht es darum zu unterscheiden,

gibt es ein Ergebnis und was ist dies, oder

gibt es gar kein Ergebnis, weil der Stream leer ist.
 
 

Die weiteren Stream-Operationen, die  Optional zurückgeben, sind  findAny() findFirst() max() und  min() .  Dabei sind die letzten beiden Convenience-Operationen, die das  reduce() ohne Anfangswert nutzen.

API

Schauen wir uns nun die generischen Klasse  Optional<T> genauer an.  In den vorhergehenden Beispielen haben wir schon gesehen, dass die Klasse im Wesentlichen ein Wrapper um ein  T ist. Nach Außen stellt die Klasse die Funktionalität zur Verfügung,
um zu erfahren, ob ein  T enthalten ist oder ob das  Optional -Objekt leer ist (Methode:  isPresent() ), und
um auf das  T zuzugreifen, falls es enthalten ist (Methode:  get() ). 

 
 

Was man noch wissen muss:  get() wirft eine  NoSuchElementException , wenn das Objekt leer ist.  Deshalb macht es Sinn (wie in unserem Beispiel oben, Zeile 4), erst mit  isPresent() zu prüfen, ob das  Optional nicht leer ist, bevor man mit  get() darauf zugreift.
 
 

Die Klasse  Optional<T> ist  final .  Das heißt, man kann nicht von ihr ableiten.  Sie hat nur zwei  private Konstruktoren und keine weiteren  public Konstruktoren. Zum Erzeugen von Instanzen muss man deshalb eine der drei statischen Factory-Methoden verwenden:
 
 

Optional<T> empty()

Optional<T> of(T value)

Optional<T> ofNullable(T value)
 
 

Um zu verstehen, was die Factory-Methoden tun, hilft es, wenn man weiß, dass die Klasse nur ein privates Feld  value vom Type  T hat.  Die Factory-Methode  empty() , die ein leeres Objekt erzeugt, initialisiert das Feld mit  null (die Details der Implementierung sind etwas komplizierter, aber das Resultat ist das gleiche). 

Die Factory-Methode  of() initialisiert das Feld mit dem übergebenen Parameter  value .  Voraussetzung dabei ist, dass  value != null ist, sonst wirft  of() eine  NullPointerException

Die Factory-Methode  ofNullable() ist lediglich eine Convenience-Methode, die sich der beiden anderen Factory-Methoden bedient.  Sie ist implementiert als:     return value == null ? empty() : of(value) ;

Wir werden später sehen, wozu man die Methode  ofNullable() benutzen kann (Abschnitt: Optional als "Bessere  n ull " ?).
 
 

Aus der Beschreibung der Factory-Methoden ergibt sich, dass  get() niemals  null zurückliefert kann, oder anders ausgedrückt:  Optional<T> kann niemals ein Wrapper um  null sein.  Streams können im Allgemeinen aber  null -Elemente enthalten, da Arrays und Collections  null -Elemente enthalten können.  Was passiert nun, wenn das Ergebnis einer Stream-Operation ein  Optional von  null sein müsste?  Was kommt zum Beispiel hier raus (das erste Element des Streams, das von  findFirst() zurückgeliefert wird, ist  null ):

Optional<String> opt = Stream.of(null, "hello", "world").findFirst();

Diese Zeile erzeugt gar kein  Optional <String> , sondern wirft vorher einer  NullPointerException .  Die  NullPointerException kommt intern aus dem Stream-Framework, wenn die Factory-Methode Optional.of() mit dem ersten Stream-Element  null als Parameter aufgerufen wird. 
 
 

Die primitiven Optional Klassen weichen etwas vom vorher Beschriebenen ab, da es für die primitiven Typen kein Äquivalent zu  null gibt. 

Fluent API

Was wir uns bisher vom API angesehen haben, reicht eigentlich vollständig aus, um den  Optional -Typ im imperativen Programmierstil zu benutzen.  Wir können:
Instanzen von  Optional konstruieren,
prüfen ob  Optional leer ist oder nicht,
auf den in  Optional enthaltenen Wert zugreifen. 

 
 

Da mit Java 8 aber Elemente der Funktionalen Programmierung und des fluent Programmierstils in Java Einzug gehalten haben, gibt es auch in  Optional weitere Methoden, die diesen Programmierstil unterstützen.  Fangen wir an mit:
 
 

void ifPresent(Consumer<? super T> consumer)
 
 

Diese Methode erlaubt es, einen  Consumer an ein  Optional anzuhängen.  Dieser konsumiert den im  Optional enthaltenen Wert, falls das  Optional nicht leer ist.  Ein Beispiel dazu:
 
 

  Stream.of(-2, -1, 0, 1, 2)

        .reduce((i, j) -> i + j)

        . ifPresent (sum -> System.out.println("sum is: " + sum));
 
 

Die Ausgabe ist wieder:   sum is: 0
 
 

Wenn wir vor der reduce-Operation wieder den Filter  filter(i -> i > 20) einbauen, bleibt die Ausgabe leer.  Denn wie vorher schon gesagt, falls das  Optional leer ist, wird der  Consumer des  ifPresent() nicht ausgeführt.
 
 

Zusätzlich gibt es drei Methoden, die darauf reagieren, wenn das  Optional leer ist:

T orElse(T other)

T orElseGet(Supplier<? extends T> other)

T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X

Die ersten beiden Methoden dienen dazu, ein Objekt vom Typ  T zu erzeugen, welches das fehlende Ergebnis ersetzt.  Die dritte Methode erlaubt es, eine Exception zu werfen, um so zu signalisieren, dass es kein Ergebnis gibt.  Schauen wir uns ein Beispiel an, das die erste Methode nutzt:
 
 

int sum = Stream.of(-2, -1, 0, 1, 2)

                .filter(i -> i > 10)      // Zeile 2

                .reduce((i, j) -> i + j)

                . orElse(0) ;
 
 

  System.out.println("sum is: " + sum);
 
 

Unabhängig davon, ob die Zeile 2 auskommentiert wird oder nicht , liefert die Ausgabe:    sum is: 0

Das heißt, die Kombination von  reduce() und  orElse() in diesem Beispiel gibt das gleiche Ergebnis wie:  reduce(0, (i, j) -> i + j) . Wenn die Zeile auskommentiert ist, ist 0 das Ergebnis der Addition.  Wenn die Zeile nicht auskommentiert ist, ist 0 der von  orElse() gesetzte Wert.
 
 

Es gibt noch weitere Methoden im API, die den fluent-Programmierstil unterstützen.  Wenn man mit Funktionaler Programmierung nicht besonders vertraut ist, versteht man sie am besten, wenn man sich kurz an unseren Einführungsartikel zu Streams erinnert (/ JAV8-4 /).  Dort haben wir erläutert, dass die Stream-Operationen (wie  filter map reduce , etc.) Funktionalität auf jedes Element des Streams anwenden.  Dabei kann der Stream kein , ein oder mehrere Elemente enthalten.  Die Funktionalität wird demgemäß nicht , einmal oder mehrmals angewandt.  Ein  Optional kann man sich ähnlich vorstellen wie einen Stream: es ist eine Abstraktion, die kein oder ein Element enthält.  Das heißt, anders als der Stream kann das  Optional nur höchstens ein Elemente enthalten und niemals mehrere.  Analog zum Stream kann man auf dem  Optional auch Operationen wie  filter map , etc. aufrufen.  Sie wenden Funktionalität auf das im  Optional enthaltene Element an; die Funktionalität wird dann nicht oder einmal ausgeführt, je nachdem, ob das Optional kein oder ein Element enthält. Die betreffenden Operationen im  Optional -API sind:

Optional<T> filter(Predicate<? super T> predicate)

Optional<U> map(Function<? super T, ? extends U> mapper)

Optional<U> flatMap(Function<? super T, Optional<U>> mapper )

Wo wir gerade dabei sind:  unter diesem Blickwinkel entspricht natürlich das  ifPresent() des  Optionals dem  forEach() des Streams. 
 
 

Die primitiven  Optional -Typen enthalten übrigens die Methoden  filter() map() flatMap() nicht. Der Grund dafür ist, dass man neben  map() und  flatMap() auf jeden Fall auch noch  mapToOb ject() und  flatMapToObj() benötigt hätte.  Dies hätte das API weiter aufgebläht und deshalb hat man die gesamte Funktionalität in den primitiven  Optional -Typen einfach weggelassen.
 
 

Schauen wir uns ein Beispiel an, dass die  map() -Methode von  Optional nutzt:
 
 

Stream<String> myStream = ...

Set<String> results = ...

   ...

Optional<Boolean> res = myStream.findAny()

                                . map (results::add);
 
 

res gibt uns Auskunft darüber, wie die vorhergehenden Arbeitsschritte ausgegangen sind.  Und zwar ist  res :

Opitional.empty , wenn  myStream leer war,

Optional[true] , wenn das gefundene Stream-Element noch nicht in dem  Set results enthalten war,

Optional[false] , wenn das gefundene Stream-Element bereits im  Set results enthalten war.
 
 

Wir haben vorher gesagt, dass wir Methoden wie  map() und ihre Funktionalität schon vom Stream API kennen.  Dazu gibt es eine kleine Einschränkung:  die Anforderungen, die es beim Stream API an die Funktionalität gibt, die man an die Methoden als Parameter übergibt, gilt natürlich jetzt nicht mehr.  So darf die  Function , die an das  map() übergibt wird, beim Stream keinen Zustand (englisch: State ) haben (/ JAV8-7 /).  Beim  Optional darf sie aber Zustand haben.  Das sieht man in unserem Beispiel: die an  map() übergebene  Function benutzt als Zustand den  Set results .

Optional als "Bessere  null " ?

Die kontroversen Diskussionen um die Klasse  Optional (/OJLD/ und /JPNG/) beruhten im Wesentlich darauf, dass es in anderen Programmiersprachen bereits Typen gab, die ähnlich zu  Optional sind, zum Beispiel  Option in Scala.  Diese Situation führte dazu, dass erwartet wurde, dass  Optional in Java den bisher in anderen Sprachen vorhandenen Typen möglichst nahe kommt.  Je nachdem wie hoch die Erwartung an dieser Stelle war, wurde sie zumindest für einige Leute nicht erfüllt. 

 
 

Die Erwartungen betrafen im Wesentlichen zwei Punkte: die Implementierung und die idiomatische Benutzung (Schlagwort: better null ).  Fangen wir mit dem Thema Implementierung an, weil es sich schneller abhandeln lässt.  Ja, die Implementierung von  Optional in Java weicht stark von der Implementierung von  Option in Scala ab.  Man hätte diesen Unterschied wohl auch kleiner machen können.  Dann wäre aber  Optional nicht so effizient geworden, wie es heute ist, oder besser gesagt: wie es in Zukunft sein soll (siehe unten, Absatz: Die Zukunft von  Optional ). 
 
 

Beim Thema " Optional als bessere  null " (englisch: better  null ) müssen wir ein wenig weiter ausholen.  Scala propagiert ein Programmieridiom, bei dem es darum geht,  Option anstelle von  null zu verwenden (/PROS/). Genauer gesagt, es gibt in Scala einen Subtyp von  Option , der  None heißt, und das Programmieridiom schlägt vor, anstelle von  null stets ein Objekt vom Typ  None zu verwenden.
 
 

Die Frage ist nun, ob und wie weit man das auch in Java machen soll. Schauen wir uns die Details an.  Es geht darum (jetzt auf Java übertragen) ein leeres  Optional anstelle von  null zu verwenden.  Betrachten wir dazu ein praktisches Beispiel: eine Version der überladenen statische Methode  getenv() aus  java.lang.System ist so deklariert:   String getenv(String name)
 
 

Die Javadoc sagt über den Returnwert: the string value of the variable, or  null if the variable is not defined in the system environment .  Wenn wir nun statt  null ein leeres  Optional Objekt zurückzugeben wollen, müssten wir den Returntyp der Methode ändern, so dass sich folgende Signatur ergibt:  Optional<String> getenv(String name)    .
 
 

Immer wenn ein Wert zu  name in der Systemumgebung gefunden wird, kommt ein  Optional<String> mit dem gefundenen Wert zurück, sonst ein leeres  Optional .  Interessant wird es, wenn wir uns überlegen, ob wir auch den Typ des Parameters  name in  Optional<String> ändern müssen.  Die Javadoc sagt über den Parameter: throws  NullPointerException – if  name is  null .  Das heißt, als Parameter darf  null nicht verwendet werden.  Deshalb passt  Optional<String> als Parametertyp nicht und der Parametertyp bleibt  String
 
 

Ein Vorteil des Idioms „ Optional als bessere  null “ ist also, dass Typen deskriptiver werden.   Optional<String> benennt einen  String -Typ, bei dem es erlaubt ist, dass der String gar nicht vorhanden ist.  St r ing hingegen bezeichnet einen Typ, bei dem man sicher sein kann, dass sein Wert immer vorhanden ist.  So kann man gleich am Typ erkennen, wo man mit fehlenden Werten rechnen muss und wo nicht.  Das führt dann hoffentlich zu stabilerem Code und vermeidet  NullPointerException s zur Laufzeit.
 
 

Ein weiterer Vorteil des " Optional als bessere- null " -Idioms ist die Vermeidung von Uneindeutigkeiten, die es heute bei der Verwendung von  null geben kann.  Das typische Beispiel für eine solche Mehrdeutigkeit ist die  get() -Methode der  java.util.M ap . Das Beispiel ist im Original auch hier (/PROS/) beschrieben.  Worum geht es?  Die  get() -Methode holt den mit einem Schlüssel assoziierten Wert aus einer  Map .  Die Signatur der Methode ist:   V get(Object key) .  Nun ist es generell erlaubt, dass zu einem Schlüssel der Wert  null abgelegt wird.  Ruft man die  get() -Methode mit einem solchen Schlüssel als Parameter auf, bekommt man natürlich  null zurück.  Gleichzeitig bedeutet der Returnwert  null aber auch, dass es keine Assoziation zu dem Schlüssel gibt, mit dem man  get() aufgerufen hat.  Das heißt, der Returnwert  null ist mehrdeutig.  Um die Uneindeutigkeit aufzulösen, ruft man typischerweise nach dem  get() noch  containsKey() auf, um zu prüfen, ob der Schlüssel in der  Map vorhanden ist. 
 
 

Wenn wir nun statt einer  Map<K,V> eine  Map<K, Optional<V>> verwenden und als assoziierten Wert anstelle von  null ein leeres  Optional verwenden, dann ist der Returnwert des  get() Aufrufs immer eindeutig! 
 
 

Das sind die wesentlichen Vorteile des Idioms „ Optional als bessere  null “.  Damit stellt sich die Frage, soll man dieses Idiom nun in Java benutzen?  Soll man vielleicht sogar bestehenden Code ändern und an dieses Idiom anpassen?  Genau das ist umstritten.
 
 

Ein wichtiger Aspekt in der Diskussion ist die Tatsache, dass Java-Programmierung nicht erst mit Java 8 anfängt und auch mit Java 8 nicht wieder von vorne anfängt.  Es gibt in Java eine mehr als achtzehnjährige Programmiertradition, die auf der Verwendung von  null basiert, und alle Java-Bibliotheken verwenden diesen Ansatz.  Wenn man mit dem „ Optional als bessere  null “-Idiom eine dieser existierenden Bibliotheken nutzen möchte, muss man Parameter immer von der  Optional -Welt des eigenen Programms in die null -Welt der Bibliothek konvertieren.  Returnwerte muss man natürlich entsprechend in die andere Richtung konvertieren. Diese Konvertierungen gehen übrigens relativ einfach und werden direkt vom  Optional API unterstützt.  Die Factory-Methode  ofNullable( T t ) konvertiert von der  null -Welt in die  Optional -Welt und der Aufruf  orElse(null) auf der entsprechenden  Optional -Instanz konvertiert in die andere Richtung.  Die Factory-Methode  ofNullable() hatte man extra noch - zu einem späteren Zeitpunkt - zum API hinzugefügt, um die Konvertierung direkt zu unterstützen.
 
 

Trotzdem gab es bei den Diskussionen im OpenJDK Lambda-Forum viel Skepsis gegenüber dem „ Optional als bessere  null “-Ansatz. Das kann unter anderem daran sehen, dass  Optional im JDK nur sehr sparsam verwendet wird.  Schauen wir uns einige Argumente und Begründungen etwas detaillier an.
 
 

Die Nutzung von  Optional bringt einen Performancenachteil, da die Instanzen von  Optional zusätzliche Wrapper um den eigentlichen Wert sind.  Die zusätzlichen Wrapper kosten Allokation und Garbage Collection. Wie groß der Performancenachteil relativ zur Gesamtperformance ist, hängt natürlich immer von der jeweiligen Situation ab.  Man war sich einig, dass der Nachteil bei der Verwendung von  Optional als Returntyp von Stream-Operationen tolerierbar ist: die Stream-Operation kostet im Allgemeinen signifikant mehr als des Erzeugen und Wegräumen der zusätzlichen  Optional -Instanz.
 
 

Ein weiteres Argument gegen „Optional als bessere  null “ war, dass man  Optional gar nicht braucht, um in Java Typen zu verwenden, die einem sagen, ob man  null benutzen darf oder nicht.  Der auf dem JSR 308 basierende Checker Framework macht genau dasselbe mit Hilfe der Typ-Annotationen  @Nullable und  @NonNull .  Der Vorteil des Checker Frameworks ist sogar, dass er keinerlei Performancenachteile mit sich bringt, da die Prüfungen statisch und nicht zur Laufzeit erfolgen.  Details zum JSR 308 und zum Checker Framework finde man hier /CFRW/.
 
 

Ob man  Optional im eigenen Code als Ersatz für  null einsetzen will, muss jeder für sich selbst entscheiden.  Der JDK verwendet  Optional derzeit sehr sparsam.  Wo und wie  Optional letztlich in der Java Community insgesamt verwendetet werden wird, bleibt abzuwarten. 

Die Zukunft von  Optional

Damit wäre eigentlich alles zu  Optional gesagt, wenn es da nicht diesen leicht kryptischen Satz in der Javadoc von  Optional gäbe: This is a value-based class; use of identity-sensitive operations (including reference equality (==), identity hash code, or synchronization) on instances of  Optional may have unpredictable results and should be avoided .  Wie soll man diesen Satz verstehen?  Er ist in gewisser Weise ein Vorgriff auf das, was mit  Optional in Zukunft vermutlich passieren wird.

 
 

Eine wahrscheinliche Ergänzung des Java-Typsystems werden Value Types sein. (Details finden sich hier /VATY/). 
 
 

Was soll das sein - ein Value Type?  Die Idee ist treffend umschrieben mit dem Mantra:  “Codes like a class, works like an int!”   Das heißt: Von der Syntax her wird ein  Value Type eher wie eine Klasse aussehen, von der Umsetzung in der JVM her soll er ähnlich zu einem primitiven Typ dargestellt und behandelt werden.

Zum Hintergrund muss man sich das Typsystem in Java ansehen.  Heute gibt es im Java-Typsystem zwei Arten von Typen: Referenztypen und primitive Typen.  Die Referenztypen verursachen erheblichen Overhead gegenüber den primitiven Typen, u.a.:

- Objekte von einem Referenztyp werden auf dem Heap angelegt; das kostet Allokation und Garbage Collection.

- Sie haben einen Objekt-Header, der u.a. auf Dinge wie das zugehörige  Class -Objekt verweist; das kostet Speicherplatz.

- Jeder Zugriff auf den Objektinhalt erfordert eine Pointer-Dereferenzierung; das kostet Performance.

- Referenztypen unterstützen Operationen wie  Object.wait() , sie können für die Synchronisation als Locks verwendet werden, und vieles mehr; das macht sie teuer, denn es verhindert Optimierungen, die der JIT-Compiler andernfalls machen könnte.

Primitive Typen kommen komplett ohne den ganzen Overhead aus.  Sie können auf dem Stack abgelegt und in Register geladen werden und sind - wenn sie unveränderlich sind - ideale Kandidaten fürs Caching auf Prozessorebene.
 
 

Die Value Types sollen ebenso effizient wie die primitiven Typen sein. Einen Value Type kann man sich vorstellen wie einen benutzer-definierten primitiven Typ, der zudem unveränderlich ist.  Außerdem hat ein Value keine Identity (d.h. keine Adresse), genauso wie ein  int keine Adresse hat.  Ein Beispiel für einen Value Type wäre ein  Point -Typ mit zwei  final int -Koordinaten.   Wenn  Point ein Value Type wäre, würde man ihn einfach als Aggregation von zwei  int s darstellen, die man auf dem Stack anlegen kann, in Register laden kann, usw.
 
 

Dieses neue Sprachmittel wird es nach Aussage von Brian Goetz auf der JAX 2014 nicht vor Java 10 geben.   Dann aber wäre  Optional ein idealer Kandidat für einen Value Type, denn  Optional ist klein und unveränderlich und braucht keine Identity.  Wenn man aber  Optional in Java 10 von einer Klasse in einen Value Type verwandelt, dann funktionieren gewisse Dinge plötzlich nicht mehr - nämlich die im oben zitierten Javadoc-Auszug genannten identity-sensitive operations .  Der Vorteil wäre jedoch, dass es keinen Performance-Overhead mehr bei der Benutzung von  Optional gäbe und damit ein gewichtiger Nachteil von  Optional weg fiele.  Genau deshalb steht der zitierte Satz vorsorglich heute schon in der Javadoc; er soll Benutzer davon abzuhalten,  Optional wie einen Referenztypen zu verwenden, damit hinterher der Umstieg auf den Value Type problemlos klappt.  (Die Diskussion, wie es zu dem Disclaimer gekommen ist, findet sich hier /ADOT/).

Zusammenfassung

In diesem Artikel haben wir haben uns die Klasse  Optional aus dem JDK 8 genauer angesehen.  Dabei handelt es sich um einen Wrapper Typ, wie man ihn in ähnlicher Form auch aus anderen (meist funktionalen) Sprachen kennt.  Deshalb gibt es neben dem imperativen check-and-get- API ( isPresent() get() ) ein umfangreiches fluent -API, das einen funktionalen Programmierstil unterstützt.  Wie diese neue Klasse in der Java Community verwendet werden wird, kann man wohl erst sehen, wenn der JDK 8 eine größere Verbreitung bekommen hat.  Performancenachteile, die sich aus der Benutzung von  Optional ergeben, könnten in Zukunft, nach der Einführung von Value Types, wegfallen.

Literaturverweise 

/ADOT/  A Disclaimer or two for Optional
URL: http://mail.openjdk.java.net/pipermail/lambda-libs-spec-observers/2013-October/002511.html
/JPNG/      
java.util.Optional<T>, Java Posse Newsgroup
URL: h ttps://groups.google.com/forum/#!topic/javaposse/fTDnkfeSHsY
/CFRW/  
Type Annotations (JSR 308) and the Checker Framework
URL: http://types.cs.washington.edu/jsr308/
/OJLD/ Diskussionen zum Thema Optional im OpenJDK lambda-dev Forum, u.a.
URL: h ttp://mail.openjdk.java.net/pipermail/lambda-dev/2012-October/006130.html
URL: http://mail.openjdk.java.net/pipermail/lambda-dev/2012-November/006654.html
/PROS/    Programming in Scala, Chapter 15.6 The Option type, Martin Odersky, et al.
Artima Press, First Edition, 2008
/VATY/ State of the Values, John Rose, et al.
URL: http://cr.openjdk.java.net/~jrose/values/values-0.html  

Die gesamte  Serie über Java 8:

/JAV8-0/ Neue Features in Java 8 - Überblick
Klaus Kreft & Angelika Langer, Java Magazin, März 2014
URL: http://www.angelikalanger.com/Articles/EffectiveJava/73.Java8.Overview/73.Java8.Overview.html
/JAV8-1/ Funktionale Programmierung in Java
Klaus Kreft & Angelika Langer, Java Magazin, September 2013
URL: http://www.angelikalanger.com/Articles/EffectiveJava/70.Java8.FunctionalProg/70.Java8.FunctionalProg.html
/JAV8-2/ Lambda-Ausdrücke und Methoden-Referenzen
Klaus Kreft & Angelika Langer, Java Magazin, Dezember 2013
URL: http://www.angelikalanger.com/Articles/EffectiveJava/71.Java8.Lambdas/71.Java8.Lambdas.html
/JAV8-3/ Default-Methoden und statische Methoden in Interfaces
Klaus Kreft & Angelika Langer, Java Magazin, Februar 2014
URL: http://www.angelikalanger.com/Articles/EffectiveJava/72.Java8.DefaultMethods/72.Java8.DefaultMethods.html
/JAV8-4/ Übersicht über das Stream API in Java 8
Klaus Kreft & Angelika Langer, Java Magazin, Mai 2014
URL: http://www.angelikalanger.com/Articles/EffectiveJava/74.Java8.Streams-Overview/74.Java8.Streams-Overview.html
/JAV8-5/ Stream-Erzeugung und Stream-Operationen
Klaus Kreft & Angelika Langer, Java Magazin, Juli 2014
URL: http://www.angelikalanger.com/Articles/EffectiveJava/75.Java8.Fundamental-Stream-Operations/75.Java8.Fundamental-Stream-Operations.html
/JAV8-6/ Stream-Kollektoren und die Stream-Operation collect()
Klaus Kreft & Angelika Langer, Java Magazin, September 2014
URL: http://www.angelikalanger.com/Articles/EffectiveJava/76.Java8.Stream-Collectors/76.Java8.Stream-Collectors.html
/JAV8-7/ Stateful Lambdas - Regeln für die Seiteneffekte in Lambda-Ausdrücken, die an Stream-Operationen übergeben werden
Klaus Kreft & Angelika Langer, Java Magazin, November 2014
URL: http://www.angelikalanger.com/Articles/EffectiveJava/77.Java8.Streams-and-Statefulness/77.Java8.Streams-and-Statefulness.html
/JAV8-8/ Das Date/Time API
Klaus Kreft & Angelika Langer, Java Magazin, Januar 2015
URL: http://www.angelikalanger.com/Articles/EffectiveJava/78.Java8.Date-Time-API/78.Java8.Date-Time-API.html
/JAV8-9/ CompletableFuture
Klaus Kreft & Angelika Langer, Java Magazin, März 2015
URL: http://www.angelikalanger.com/Articles/EffectiveJava/79.Java8.CompletableFuture/79.Java8.CompletableFuture.html
/JAV8-10/ Optional<T>
Klaus Kreft & Angelika Langer, Java Magazin, Mai 2015
URL: http://www.angelikalanger.com/Articles/EffectiveJava/80.Java8.Optional-Result/80.Java8.Optional-Result.html
/JAV8-11/ Parallel Streams
Klaus Kreft & Angelika Langer, Java Magazin, Juli 2015
URL: http://www.angelikalanger.com/Articles/EffectiveJava/81.Java8.Parallel-Streams/81.Java8.Parallel-Streams.html
/JAV8-12/ Das Performance-Modell der Streams
Klaus Kreft & Angelika Langer, Java Magazin, September 2015
URL: http://www.angelikalanger.com/Articles/EffectiveJava/82.Java8.Performance-Model-of-Streams/82.Java8.Performance-Model-of-Streams.html
/JAV8-13/ reduce() vs. collect()
Klaus Kreft & Angelika Langer, Java Magazin, November 2015
URL: http://www.angelikalanger.com/Articles/EffectiveJava/83.Java8.Reduce-vs-Collect-Stream-Operations/83.Java8.Reduce-vs-Collect-Stream-Operations.html
/JAV8-14/ User-Defined Collectors
Klaus Kreft & Angelika Langer, Java Magazin, Januar 2016
URL: http://www.angelikalanger.com/Articles/EffectiveJava/84.Java8.User-Defined-Stream-Collectors/84.Java8.User-Defined-Stream-Collectors.html
/JAV8-15/ Parallele Streams und Blockierende Funktionalität
Klaus Kreft & Angelika Langer, Java Magazin, März 2016
URL: http://www.angelikalanger.com/Articles/EffectiveJava/85.Java8.Streams-and-Blocking-Functionality/85.Java8.Streams-and-Blocking-Functionality.html
/JAV8-16/ API-Design mit Lambdas
Klaus Kreft & Angelika Langer, Java Magazin, Mai 2016
URL: http://www.angelikalanger.com/Articles/EffectiveJava/86.Java8.API-Design-With-Lambdas/86.Java8.API-Design-With-Lambdas.html
/JAV8-17/ Low-Level-Aspekte beim API Design mit Lambdas
Klaus Kreft & Angelika Langer, Java Magazin, Juli 2016
URL: http://www.angelikalanger.com/Articles/EffectiveJava/87.Java8.Programming-With-Lambdas/87.Java8.Programming-With-Lambdas.html
/JAV8-18/ Benutzer-definierte Stream-Sourcen und Spliteratoren
Klaus Kreft & Angelika Langer, Java Magazin, September 2016
URL: http://www.angelikalanger.com/Articles/EffectiveJava/88.Java8.User-Defined-Stream-Sources-And-Spliterators/88.Java8.User-Defined-Stream-Sources-And-Spliterators.html

 
 

If you are interested to hear more about this and related topics you might want to check out the following seminar:
Seminar
Lambdas & Streams - Java 8 Language Features and Stream API & Internals
3 day seminar ( open enrollment and on-site)
Java 8 - Lambdas & Stream, New Concurrency Utilities, Date/Time API
4 day seminar ( open enrollment and on-site)
Effective Java - Advanced Java Programming Idioms 
4 day seminar ( open enrollment and on-site)
 
Related Reading
Lambda & Streams Tutorial & Reference
In-Depth Coverage of all aspects of lambdas & streams
Lambdas in Java 8
Conference Presentation at JFokus 2012 (slides)
Lambdas in Java 8
Conference Presentation at JavaZone 2012 (video)
 

 
 
  © Copyright 1995-2018 by Angelika Langer.  All Rights Reserved.    URL: < http://www.AngelikaLanger.com/Articles/EffectiveJava/80.Java8.Optional-Result/80.Java8.Optional-Result.html  last update: 26 Oct 2018