Optional în Java

O „cutie” care conține o valoare sau este goală. Arată clar că un rezultat poate lipsi și te obligă să tratezi cazul, în loc de null.

Sintaxă
import java.util.Optional;

public final class Optional<T>

Optional<T> o = Optional.of(valoare);
Optional<T> o = Optional.ofNullable(valoarePosibilNula);
Optional<T> o = Optional.empty();

Ce face Optional

Optional<T> este un obiect care fie conține o valoare de tip T, fie este gol. Metodele care pot să nu găsească nimic îl returnează în loc de null: stream.findFirst(), stream.max(...), propriile tale metode de căutare. Tipul returnat spune clar „rezultatul poate lipsi”.

Nu scoți valoarea direct, ci spui ce se întâmplă în ambele cazuri: orElse(rezerva), orElseGet(...), orElseThrow(), ifPresent(...), ifPresentOrElse(...). Poți transforma valoarea fără să o scoți: map, flatMap, filter.

Optional este gândit pentru valori de retur. Nu îl folosi pentru câmpuri, parametri sau elemente de colecții; acolo o colecție goală sau o valoare implicită sunt mai potrivite. Pentru primitive există OptionalInt, OptionalDouble, OptionalLong.

Parametri

Parametru Descriere
T Tipul valorii din interior.

Ce returnează

Un obiect Optional, plin sau gol.

Exemple

Exemplul 1: Căutarea unui utilizator

import java.util.Map;
import java.util.Optional;

public class Main {
    static final Map<String, String> emailuri = Map.of("ana", "ana@exemplu.ro", "radu", "radu@exemplu.ro");

    static Optional<String> gasesteEmail(String utilizator) {
        return Optional.ofNullable(emailuri.get(utilizator));
    }

    public static void main(String[] args) {
        System.out.println(gasesteEmail("ana"));
        System.out.println(gasesteEmail("elena"));
        System.out.println(gasesteEmail("radu").orElse("fără email"));
        System.out.println(gasesteEmail("elena").orElse("fără email"));
    }
}

Rezultat

Optional[ana@exemplu.ro]
Optional.empty
radu@exemplu.ro
fără email

Exemplul 2: Optional din stream-uri

import java.util.Comparator;
import java.util.List;
import java.util.Optional;

public class Main {
    public static void main(String[] args) {
        List<String> orase = List.of("Iași", "Cluj", "Constanța", "Craiova");
        Optional<String> primulCuC = orase.stream().filter(o -> o.startsWith("C")).findFirst();
        Optional<String> primulCuZ = orase.stream().filter(o -> o.startsWith("Z")).findFirst();
        primulCuC.ifPresent(o -> System.out.println("Găsit: " + o));
        System.out.println("Cu Z: " + primulCuZ.isPresent());
        System.out.println("Cel mai lung: " + orase.stream().max(Comparator.comparingInt(String::length)).orElseThrow());
    }
}

Rezultat

Găsit: Cluj
Cu Z: false
Cel mai lung: Constanța

Exemplul 3: Lanț de transformări

map lucrează doar dacă există valoare; altfel tot lanțul rămâne gol și ajunge la orElse.

import java.util.Optional;

public class Main {
    record Adresa(String oras) {}
    record Client(String nume, Adresa adresa) {}

    static String orasMajuscule(Client c) {
        return Optional.ofNullable(c)
                .map(Client::adresa)
                .map(Adresa::oras)
                .map(String::toUpperCase)
                .orElse("NECUNOSCUT");
    }

    public static void main(String[] args) {
        System.out.println(orasMajuscule(new Client("Ana", new Adresa("Iași"))));
        System.out.println(orasMajuscule(new Client("Radu", null)));
        System.out.println(orasMajuscule(null));
    }
}

Rezultat

IAȘI
NECUNOSCUT
NECUNOSCUT

Greșeli frecvente

  • Apelezi get() fără să verifici. Pe un Optional gol aruncă NoSuchElementException; folosește orElse, orElseThrow sau ifPresent.
  • Returnezi null dintr-o metodă al cărei tip este Optional. Returnează Optional.empty().
  • Folosești Optional pentru câmpuri și parametri. Nu este gândit pentru asta și complică codul.

Comenzi înrudite

  • Optional.of: of(x) creează un Optional cu o valoare sigur prezentă, ofNullable(x) unul care e gol dacă x este null, empty() unul gol.
  • Optional.orElse: orElse returnează valoarea sau o rezervă dată; orElseGet calculează rezerva cu o funcție, doar când e nevoie.
  • Optional.map: map transformă valoarea din Optional (dacă există) și o pune într-un Optional nou; flatMap face la fel pentru funcții care returnează deja Optional.
  • Optional.ifPresent: ifPresent execută o acțiune doar dacă există valoare; ifPresentOrElse are și o acțiune separată pentru cazul gol.
  • Stream.findFirst: Returnează primul element din stream (findFirst) sau oricare element (findAny), ca Optional.
  • Map.get: get returnează valoarea unei chei sau null dacă lipsește; getOrDefault returnează o valoare de rezervă în loc de null.