18_Optional

Videos

DateinameAktion
VIDEOVideo_Student_DAbspielen

Lernmaterialien

Optional

Allgemein

In Java ist Optional ein Container-Objekt, das ausdrückt:

„Hier könnte ein Wert vorhanden sein – oder eben nicht.“

Es wird oft benutzt, um null sauberer und sicherer zu behandeln.

Beispiel

Statt so:

String name = htl.minAlter();
if (name != null) {
    System.out.println(name);
}

kann man schreiben:

Optional<String> name = htl.minAlter();
name.ifPresent(System.out::println);

Was steckt drin?

Ein Optional<T> enthält entweder:

  • einen Wert vom Typ T

  • keinen Wert

Wichtige Methoden

Optional<String> opt = Optional.of("Hallo");

oder leer:

Optional<String> leer = Optional.empty();

Häufige Methoden:

  • isPresent() → prüft, ob ein Wert da ist

  • ifPresent(...) → führt etwas aus, wenn ein Wert da ist

  • orElse(...) → liefert Standardwert, falls leer

  • orElseGet(...) → liefert alternativ berechneten Wert

  • orElseThrow(...) → wirft Exception, falls leer

  • map(...) → verarbeitet den Wert weiter

  • filter(...) → behält den Wert nur bei erfüllter Bedingung

Beispiele

Optional<String> opt = Optional.of("Max");

System.out.println(opt.isPresent());         // true
System.out.println(opt.orElse("Unbekannt")); // Max

Leeres Optional:

Optional<String> opt = Optional.empty();

System.out.println(opt.isPresent());         // false
System.out.println(opt.orElse("Unbekannt")); // Unbekannt

Mit map:

Optional<String> opt = Optional.of("Max");
Optional<Integer> laenge = opt.map(String::length);

System.out.println(laenge.orElse(0)); // 3

Warum benutzt man Optional?

Vorteile:

  • macht klar, dass ein Wert fehlen kann

  • reduziert NullPointerException

  • zwingt zu bewusster Behandlung fehlender Werte

  • oft lesbarer als viele null-Prüfungen

Wichtiger Hinweis

Optional wird vor allem als Rückgabetyp verwendet, nicht typischerweise für:

  • Felder in Klassen

  • Methodenparameter

Also eher so:

public Optional<Kunde> getStudent(int pos)

als so:

public void setKunde(Optional<Kunde> kunde)

Kurz gesagt

Optional bedeutet in Java:

Ein Wert ist optional vorhanden.

Es ist eine Alternative zu null, um fehlende Werte sicherer und ausdrücklicher zu behandeln.


Methoden

Optional.of(wert)

Für einen sicher vorhandenen Wert.

Ist wert == null, dann gibt es sofort eine Exception.

Gerne:

Das hier wäre falsch:

String name = null;
Optional<String> opt = Optional.of(name); // Fehler

Optional.ofNullable(wert)
Für einen Wert, der auch null sein darf.
Wenn wert null ist, entsteht einfach ein leeres Optional.

String name = null;
Optional<String> opt = Optional.ofNullable(name);

Optional.empty()

Erzeugt direkt ein leeres Optional.

Optional<String> opt = Optional.empty();

Merksatz:

  • of(...) → Wert muss da sein

  • ofNullable(...) → Wert kann null sein

  • empty()gar kein Wert


Beispiele

avgAlter()

    public double avgAlter()
    {
        int summe;

        if (sessel.size() > 0)
        {
            summe = 0;
            for (Student s : sessel)
            {
                summe += s.getAlter();
            }
            return (double)summe / sessel.size();
        }
        else
        {
            return -999;
        }
    }

Hier ist Optional sinnvoll, weil -999 nur ein künstlicher Fehlerwert ist.

Das Problem an -999:

  • fachlich ist das kein sinnvolles Durchschnittsalter

  • der Aufrufer muss wissen, dass -999 „kein Ergebnis“ bedeutet

  • das ist fehleranfällig

OptionalDouble ist die bessere Lösung, da die Methode einen double liefert kann.

public OptionalDouble avgAlter() {
    if (sessel.size() > 0) {
        int summe = 0;

        for (Student s : sessel) {
            summe += s.getAlter();
        }

        return OptionalDouble.of((double) summe / sessel.getAnzahl());
    }
    else {
        return OptionalDouble.empty();
    }
}

Warum OptionalDouble?

Für primitive Typen gibt es spezielle Varianten:

  • OptionalInt

  • OptionalLong

  • OptionalDouble

Das ist meist passender als Optional<Double>.

Noch etwas kürzer

import java.util.OptionalDouble;

public OptionalDouble avgAlter() {
    if (sessel.isEmpty()) {
        return OptionalDouble.empty();
    }

    int summe = 0;
    for (Student s : sessel) {
        summe += s.getAlter();
    }

    return OptionalDouble.of((double) summe / sessel.getAnzahl());
}

Verwendung

OptionalDouble avg = htl.avgAlter();

if (avg.isPresent()) {
    System.out.println(avg.getAsDouble());
}
else {
    System.out.println("Keine Studenten vorhanden");
}

oder:

System.out.println(htl.avgAlter().orElse(0.0));

Mit Streams

Das ist besonders elegant, weil average() direkt ein OptionalDouble liefert.

public OptionalDouble avgAlter() {
    return sessel.stream()
                 .mapToInt(Student::getAlter)
                 .average();
}

Das ist besonders elegant, weil average() direkt ein OptionalDouble liefert.


maxAlter()

    public Student maxAlter()
    {
        int max;
        Student maxStudent;

        max = -999;
        maxStudent = null;
        for (Student s : sessel)
        {
            if (s.getAlter() > max)
            {
                max = s.getAlter();
                maxStudent = s;
            }
        }
        return maxStudent;
    }

maxAlter() liefert einen Optional<Student>, weil die Methode kein Ergebnis haben kann, wenn sessel leer ist.

Umbau mit Optional<Student>

public Optional<Student> maxAlter() {
    Student maxStudent = null;

    for (Student s : sessel) {
        if (maxStudent == null || s.getAlter() > maxStudent.getAlter()) {
            maxStudent = s;
        }
    }

    return Optional.ofNullable(maxStudent);
}

Die erste Version hat zwei typische Probleme:

  • -999 ist wieder ein künstlicher Startwert

  • maxStudent kann null bleiben, wenn sessel leer ist

Mit Optional<Student> wird klarer:

Es gibt vielleicht einen ältesten Studenten, vielleicht aber auch keinen.

Verwendung

Optional<Student> opt = maxAlter();

opt.ifPresent(s -> System.out.println(s.getName()));

oder:

Student s = maxAlter().orElse(null);

oder:

System.out.println(
    maxAlter()
        .map(Student::getName)
        .orElse("Kein Student vorhanden")
);

Mit Streams

Falls Streams schon Thema sind:

Das ist sehr elegant, weil max(...) direkt ein Optional<Student> zurückgibt.


Parameter

public void aufnehmen(Student wen) {
    if (wen != null) {
        if (sessel.contains(wen) == false)
            sessel.add(wen);
        else
            System.out.println("Fehler: schon in dieser Schule!");
    }
    else {
        System.out.println("Fehler: kein Student!");
    }
}
public void aufnehmen(Optional<Student> wen) {
    if (wen.isEmpty()) {
        System.out.println("Fehler: kein Student!");
        return;
    }

    Student student = wen.get();

    if (sessel.contains(student)) {
        System.out.println("Fehler: schon in dieser Schule!");
        return;
    }

    sessel.add(student);
}

Warum ist Optional hier nicht ideal?

So etwas wäre zwar möglich:

public void aufnehmen(Optional<Student> wen)

aber das ist meistens keine gute Lösung, weil der Aufrufer dann so etwas machen müsste:

aufnehmen(Optional.of(student));  
aufnehmen(Optional.empty());

Das macht den Aufruf eher umständlicher als besser.

Optional ist als Parameter nicht sinnvoll!

Optional wird in Java hauptsächlich als Rückgabewert verwendet, nicht für Methodenparameter.