ObrázokUž to tak bude, že v každom programovacom jazyku (a Java nie je výnimkou) potrebujete raz za čas riešiť jeden problém. Ten problém je, že máte metódu, ktorá vracia nejaký výsledok (presnejšie objekt alebo ešte presnejšie jeho referenciu), ale môže sa vyskytnúť legálny prípad, kedy metóda nevráti nič. V takom prípade sa najčastejšie siaha po riešení, že metóda vráti hodnotu null. A to je riešenie, ktoré vie priniesť viac problémov ako úžitku. Volajúci kód totiž s niečím takým jednoducho nemusí počítať a výsledok je známa NullPointerException. Tento problém je tu s nami už nejaký čas a rôzne jazyky (a Java nie je výnimkou) sa s ním snažia rôzne vysporiadať. Dnes náš seriál o Jave 8 bude o novom type Optional.

Problém, ktorý som popísal v úvode, sa snaží napr. Groovy riešiť pomocou Null-Safe Dereference operátora. Môžem to vyzerať napríklad takto:

String version = computer?.getSoundcard()?.getUSB()?.getVersion();

getSoundcard bude volané, ak computer nieje null. getUSB bude volané, len ak getSoundcard nevráti null atď. Rovnakú syntax používa tiež C#. Haskell má na tento problém typ Maybe, Scala typ Optional[T] a Java? Java mala dlho knižnicu Guava, ktorá Optional obsahovala. Od verzie 8 je už súčasťou jej samotnej.

Čo to Optional vlastne je? Je to typový kontajner, ktorý môže alebo nemusí obsahovať referenciu na objekt. Jeho použite je pomerne jednoduché a najlepšie sa demonštruje na návratovej hodnote nejakej metódy. Tá nevráti samotný objekt, ale objekt Optional. Napríklad:

Optional<Car> methodResult = getCarObject();

S kontajnerom Optional viem následne pracovať. Napríklad zistiť, či niečo obsahuje:

methodResult.isPresent();

Alebo vytiahnuť z neho objekt:

methodResult.get();

To samozrejme nie je všetko. API objektu obsahuje metódy, ktoré uľahčujú prácu s ním na základe toho, či nejaký objekt obsahuje alebo nie. Napríklad zavolať funkciu, ak niečo obsahuje:

methodResult.isPresent(System.out::println);

Alebo vrátiť nejaký default objekt, ak je naopak kontajner prázdny:

methodResult.orElse(new Car());

A čo tak vyhodiť výnimku, ak je prázdny:

methodResult.orElseThrow(IllegalStateException::new);

Optional API má v sebe dokonca niekoľko metód, ktoré sú prebraté z API streamov. Napríklad:

methodResult.map(String::trim)
      .filter(t -> t.length() > 1)
      .ifPresent(System.out::println);

Celé to funguje tak, že ak Optional obsahuje nejakú hodnotu, tak sa na ňu aplikuje postupne map a následne filter operácia. Výsledkom filter operácie je, že buď Optional ostane nejakou hodnotou naplnený (ak vyhovuje filtru) alebo nie. Na záver, ak v ňom ešte stále je nejaká hodnota, vytlačí sa obsah do konzoly.

Na Optional sa dá pozerať ako na šikovnú krabičku, v ktorej si viete prenášať nejaký odkaz a je to vo viacerých ohľadoch praktickejšie ako keď použíte priamo ten odkaz. Neznamená to, že teraz by všetky vaše metódy mali vracať už len Optional. To ani je je oficiálne odporúčanie. Je dobré to ale používať v public API, kde chcete tomu, čo váš kód bude volať, jasne naznačiť, že možno mu nič nevrátite. Ak už mu totiž metóda raz vracia takýto objekt, nedá sa tak ľahko ignorovať ako obyčajna poznámka v dokumentácii, že metóda môže vrátiť aj null.