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);
}
}
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()
}