Optional

null is just the absence of value and NullPointerException is 1 of the most common exceptions every java developer has to deal with.

"I call it my billion-dollar mistake." – Sir C. A. R. Hoare, on his invention of the null reference

Most of the methods either customer or library return null if there is no result which they mention in the document as well that this method may return null or throw NullPointerException. Developers are supposed to check all the variables before actually using or applying any other function over it.

you are not a real Java programmer until you've dealt with a null pointer exception.

So many times, variables returned from another method, DAO layer or API response will have null values, sometimes the developer checks for null and most of the time forget, which results into NullPointerException, like below.

package org.wesome.java8;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

class Apple {

    public static void main(String[] args) {
        /*  get value from some other method    */
        String appleName = getApple(5);
        /*  null check before using */
        if (Objects.nonNull(appleName)) {
            System.out.println("I Love " + appleName.toUpperCase() + " Apple");
        }
        /*  get value from some other method    */
        appleName = getApple(5);
        /*  use without null check */
        System.out.println("I Love " + appleName.toUpperCase() + " Apple");
    }

    /*  another method, or dao layer which might return null    */
    public static String getApple(int index) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Macintosh");
        map.put(2, "Fuji");
        map.put(3, "Gala");
        map.put(4, "Jonagold");
        String result = map.get(index);

        return Objects.nonNull(result) ? result : null;
    }
}

Java 8 introduced a new Optional class in java.util package to deal with NullPointerException. instead of returning null directly, it returns a non-null reference of Optional Object which will hold the empty or non-null reference.

Optional never contains null, instead, it has Empty.

Optional dosent gurantee to resolve NullPointerException, it's an attempt to reduce the passing of null reference directly. it makes responses more expressive and removes a lot of null check boilerplate code. Optional makes developers think about NullPointerException. it increases the readability of code as well that the variable might be null.

Most Stream methods such as findFirst, FindAny return Optional

Optional will check the presence of value in the variable. The Optional class provides 3 main methods

  • Optional.empty
  • Optional.of
  • Optional.ofNullable

Optional will not give results directly, which will remind the developer to check for null using isPresent or isAbsent method.

Optional Empty

Optional Empty is the replacement for null, instead of creating a reference or variable with null, create using Optional, so the absence of value will return Empty instead of null
Optional class defines EMPTY reference as final and static

private static final Optional<?> EMPTY = new Optional<>(null);

so only one instance will be created throughout the application and will be loaded into memory at the bootup time, the default constructor is private so an Optional instance can be created using the 3 methods provided above only.

package org.wesome.java8;

import java.util.Optional;

class Apple {

    public static void main(String[] args) {
        String appleName = null;
        Optional<String> applePrice = Optional.empty();
    }
}

Optional Empty returns the empty instance of the Optional class when there is no result.

package org.wesome.java8;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

class Apple {

    public static void main(String[] args) {
        Optional<String> appleName = getApple(4);
        if (appleName.isPresent()) {
            System.out.println("I Love " + appleName.get().toUpperCase() + " Apple");
        }
        appleName = getApple(5);
        if (appleName.isPresent()) {
            System.out.println("I Love " + appleName.get().toUpperCase() + " Apple");
        }
    }

    /*  another method, or dao layer which might return null    */
    public static Optional<String> getApple(int index) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Macintosh");
        map.put(2, "Fuji");
        map.put(3, "Gala");
        map.put(4, "Jonagold");
        String value = map.get(index);

        Optional<String> result = Objects.nonNull(value) ? Optional.of(value) : Optional.empty();
        return result;
    }
}

Optional Of

Sometimes the null check is required, and if the value is null then throw an Exception immemorially, Optional class provides Optional.of the method will check for null values, and throw NullPointerException.

Optional Of should only be used when value is surely not null

package org.wesome.java8;

import java.util.Optional;

class Apple {

    public static void main(String[] args) {
        String appleName = "fuji";
        /*  appleName variable contains non-null value, hence Optional.of will not throw Exception   */
        Optional.of(appleName);

        /*  appleName variable contains null value, hence Optional.of will throw Exception   */
        appleName = null;
        Optional.of(appleName);
    }
}

Optional ofNullable

The Optional class provided the Optional.ofNullable method to hold the variable, it may contain a non-null value or an empty value if the value is null.

Optional ofNullable should be used when the value may or may not be null

package org.wesome.java8;

import java.util.Optional;

class Apple {

    public static void main(String[] args) {
        /*  appleName is null, hence Optional.empty */
        String appleName = null;
        Optional<String> apple = Optional.ofNullable(appleName);
        System.out.println("apple = " + apple);

        /*  appleName is non-null, hence Optional [value]   */
        appleName = "Fuji";
        apple = Optional.ofNullable(appleName);
        System.out.println("apple = " + apple);
    }
}

isPresent

An Optional class will hold non-null or empty variables. decisions have to be made based on the presence or absence of value. to check for the presence or absence of value, the Optional class provides isPresent method.

the isPresent method will return true or false depending upon the presence or absence of value, however, the use of isPresent is discouraged because it doesn't provide much improvement over traditional if-else.

package org.wesome.java8;

import java.util.Optional;

class Apple {
    public static void main(String[] args) {
        String appleName = "Fuji";
        Optional<String> apple = Optional.ofNullable(appleName);

        if (apple.isPresent()) {
            System.out.println("apple = " + apple.get());
        } else {
            System.out.println("apple is empty");
        }
    }
}

ifPresent

isPresent returns true or false based on the presence or absence of value, but most of the time, execution will only happen if the value is present, so to avoid if-else of ifPresent method, the Optional class provides ifPresent method. ifPresent will internally check for the presence of value and proceed only if the value is present.

package org.wesome.java8;

import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;

class Apple {
    public static void main(String[] args) {
        Optional<String> apple = Optional.ofNullable("Fuji");

        /*  print value using traditional if-else   */
        if (Objects.nonNull(apple)) {
            System.out.println(apple.get());
        }

        /*  print value using Optional isPresent    */
        if (apple.isPresent()) {
            System.out.println(apple.get());
        }

        /*  print value using Optional ifPresent via method reference   */
        apple.ifPresent(System.out::println);

        /*  print value using Optional ifPresent via Consumer Functional Interface   */
        Consumer<String> consumer = System.out::println;
        apple.ifPresent(consumer);
    }
}

filter

Streams provide a filter method to filter out the elements same way Optional also provides a filter method to filter value. Filter method is null safe and will not throw NullPointerExcepiton even if the value is null, it will check for the presence of value in a variable, and if true then only proceed further.

package org.wesome.java8;

import java.util.Objects;
import java.util.Optional;

class Apple {
    public static void main(String[] args) {

        Optional<String> tree = Optional.empty();
        Optional<String> fruit = Optional.ofNullable(null);
        Optional<String> apple = Optional.ofNullable("Apple");
        String fuji = null;
        /*  traditional approach */
        if (Objects.nonNull(fruit) && fuji.equals("Fuji")) {
            System.out.println(fuji);
        }
        /*  Optional approach    */
        tree.filter(t -> t.equals("Fuji")).ifPresent(System.out::println);
        fruit.filter(f -> f.equals("Fuji")).ifPresent(System.out::println);
        apple.filter(a -> a.equals("Fuji")).ifPresent(System.out::println);
    }
}

 

orElse orElseGet and orElseThrow

orElse method in the Optional class returns an alternative value if the actual value is null.

package org.wesome.java8;

import java.util.Objects;
import java.util.Optional;

class Apple {
    public static String DEFAULT_APPLE = "Macintosh";

    public static void main(String[] args) {
        String appleTemp;

        /* null check using traditional if-else*/
        String appleName = "Fuji";
        if (Objects.nonNull(appleName)) {
            appleTemp = appleName;
            System.out.println("appleName = " + appleTemp);
        }

        /* null check using Optional or else */
        Optional<String> apple = Optional.ofNullable("Fuji");
        appleName = apple.orElse(DEFAULT_APPLE);
        System.out.println("appleName = " + appleName);

        apple = Optional.ofNullable(null);
        appleName = apple.orElse(DEFAULT_APPLE);
        System.out.println("appleName = " + appleName);
    }
}

orElse will return the static value but sometimes a dynamic value is required in case of absence of variable, for those situations, Optional provides OrElseGet method, it takes an Supplier which will return dynamic values.

package org.wesome.java8;

import java.time.LocalDate;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;

class Apple {

    public static void main(String[] args) {

        Supplier<LocalDate> DEFAULT_DATE = () -> LocalDate.now();

        LocalDate date = LocalDate.of(2020, 1, 1);

        /* null check using traditional if-else */
        if (Objects.nonNull(date)) {
            System.out.println("i ate apple on " + date);
        } else {
            System.out.println("i ate apple on " + DEFAULT_DATE);
        }

        /* null check using Optional or else Get */
        Optional<LocalDate> apple = Optional.ofNullable(date);
        System.out.println("i ate apple on " + apple.orElseGet(DEFAULT_DATE));

        apple = Optional.ofNullable(null);
        System.out.println("i ate apple on " + apple.orElseGet(DEFAULT_DATE));
    }
}

if an Exception needs to be thrown if the value is empty then the Optional class provides orElseThrow method.

package org.wesome.java8;

import java.util.Optional;
import java.util.function.Supplier;

class Apple {
    public static void main(String[] args) throws Exception {
        Supplier<Exception> supplier = NoSuchFieldException::new;

        /* orElseThrow using Lambda Expression  */
        Optional<String> apple = Optional.ofNullable("Fuji");
        String appleName = apple.orElseThrow(NoSuchFieldException::new);
        System.out.println("appleName = " + appleName);

        /* orElseThrow using Method Reference  */
        apple = Optional.ofNullable(null);
        appleName = apple.orElseThrow(supplier);
        System.out.println("appleName = " + appleName);
    }
}

Map

Stream provides a map method to transform data from 1 state to another, the same way Optional also provides a map function to transform data, Optional and filter will check for the presence of data.

package org.wesome.java8;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

class Apple {
    public static void main(String[] args) {
        List<Optional<String>> appleName = Arrays.asList(
                Optional.of("Macintosh"),
                Optional.empty(),
                Optional.of("Fuji"),
                Optional.empty(),
                Optional.of("Gala"),
                Optional.empty(),
                Optional.of("Jonagold"));
        
        appleName.stream()
                .filter(Objects::nonNull)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .map(String::toUpperCase)
                .forEach(System.out::println);
    }
}

Iterate List of Optional Strings

a lot of times Collection of strings needs to be iterated and printed if elements are not null. the traditional way is to loop over the Collection, check for null and print accordingly, but using Stream and Optional it can be done easily.

package org.wesome.java8;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

class Apple {
    public static void main(String[] args) {
        /*  iterate over traditional list of string */
        List<String> apples = Arrays.asList("Macintosh", null, "Fuji", "Gala", "Jonagold");
        apples.stream().map(Optional::ofNullable).filter(Optional::isPresent).forEach(System.out::println);

        /*  iterate over list of Optional String */
        List<Optional<String>> appleName = Arrays.asList(
                Optional.of("Macintosh"),
                null,
                Optional.of("Fuji"),
                Optional.empty(),
                Optional.of("Gala"),
                null,
                Optional.of("Jonagold"));

        appleName.stream().filter(Optional::isPresent).map(Optional::get).forEach(System.out::println);
    }
}

 

body_4

Optional as Class Members

Java does recommend using Optional as Class Members to avoid the NullPointerException. but make sure to not use the Optional parameter in the Serializable class.

package org.wesome.java8;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.Optional;

@Getter
@AllArgsConstructor
class Tree {
    private Optional<Fruit> fruit;

}

@Getter
@AllArgsConstructor
class Fruit {
    private Optional<Apple> apple;

}

@Getter
@AllArgsConstructor
class Apple {
    private Optional<Macintosh> macintosh;

}

@Getter
@AllArgsConstructor
class Macintosh {
    public static String DEFAULT_APPLE = "Fuji";
    private String apple;

    public static void main(String[] args) {

        Tree tree = new Tree(Optional.ofNullable(new Fruit(Optional.ofNullable(new Apple(Optional.ofNullable(new Macintosh("Macintosh")))))));

        String name = tree.getFruit()
                .flatMap(Fruit::getApple)
                .flatMap(Apple::getMacintosh)
                .map(Macintosh::getApple)
                .orElse(DEFAULT_APPLE);

        System.out.println("Apple Name = " + name);
    }
}
plugins {
    id 'java'
    id "io.freefair.lombok" version "6.2.0"
}

group 'org.example'
version '1.0-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
}

test {
    useJUnitPlatform()
}

Optional with Serialization

Optional are designed to be used as a return type to avoid NullPointerException, it should not be used with classes which might transfer over the network because it cannot be Serialised and will throw a NotSerializableException.

POJOs, Entity Beans, Data Models, and DTOs should use traditional getters

package org.wesome.java8;

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Optional;

@AllArgsConstructor
@NoArgsConstructor
class Fruit implements Serializable {
    private Optional<String> apple;
    private String taste;
}

class Apple {
    public static void main(String[] args) {
        try (FileOutputStream fileOutputStream = new FileOutputStream("fruit.txt");
             ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) {

            Fruit fruit = new Fruit(Optional.ofNullable("Fuji"), "sweet");
            objectOutputStream.writeObject(fruit);
            objectOutputStream.flush();
            System.out.println("success");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
plugins {
    id 'java'
    id "io.freefair.lombok" version "6.2.0"
}

group 'org.example'
version '1.0-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
}

test {
    useJUnitPlatform()
}

 

follow us on