preface
The Optional class introduced in Java 8 to solve nullPointerExceptions and tedious NULL checking first appeared in Guava. Java 8 has just become part of the class library.
An introduction to
Optional is a class that encapsulates values that hold values of type T; So Optional is essentially a container.
For example, a Person may or may not have a car, so the car variable inside the Person class should not be declared as car. When the variable exists, the Optional class simply encapsulates the car. If the variable does not exist, an empty Optional object is returned using the option.empty () method. As follows:
But what’s the essential difference between null references and option.empty ()? Semantically, they can be considered the same thing, but in practice they are very different: If you try to dereference a NULL, a NullPointerException must be triggered, but using optional.empty () is a valid object.
Let’s take a look at what Optional provides.
create
When it comes to Optional functionality, let’s first look at creating Optional instances.
Empty Optional
As mentioned earlier, you can create an empty Optional object using the static factory method option. empty:
Optional<Car> option = Optional.empty();
Copy the code
Because empty() itself represents an empty object, calling the get method throws NoSuchElementException.
Non-null Optional
You can also use the static factory method option. of to create an Optional object from a non-null value:
Optional<Car> optional = Optional.of(car);
Copy the code
If car is a NULL, this code throws a NullPointerException immediately, rather than waiting until you try to access the car attribute value and return an error.
Optional that can be null
Finally, using the static factory method option. ofNullable, you can create an Optional object that allows null values:
Optional<Car> optional = Optional.ofNullable(car);
Copy the code
If car is null, the resulting Optional object is empty. We can take a look at how it works:
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
Copy the code
Based on how it is implemented, we know that Optional. Empty () is returned when the value passed is null. This allows us to encapsulate values that might be null. For example, if there is an instance of Map
that accesses the key index, a NULL is returned if there is no value associated with the key. Therefore, we can use the optional. ofNullable method to encapsulate the return value:
Optional<Object> value = Optional.ofNullable(map.get("key"));
Object value = map.get("key");
Copy the code
This replaces the potential null bug with an empty Optional object.
operation
Now that we have created the Optional instance, we need to operate on it.
isPresent & get
In the Optional class, the isPresent method checks whether the Optional instance contains a value and returns true if it does, false otherwise. As opposed to the isEmpty method there’s also the get method in the Optional class, which is used to get the value in the Optional instance.
Optional<String> optional = Optional.of("is present");
if (optional.isPresent()) {
System.out.println("the value is " + optional.get());
}
Copy the code
IsPresent is generally used in combination with GET to avoid nullPointerExceptions:
public boolean isPresent(a) {
returnvalue ! =null;
}
public T get(a) {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
Copy the code
If isPresent is not used, null pointer exceptions may occur. If isPresent is not used, null pointer exceptions may occur. But this approach is different from if(null! = value) makes no difference, so we should try to avoid this combination.
ifPresent
In addition to isPresent’s concise method, Optional also provides an interface to receive functional arguments ifPresent:
public void ifPresent(Consumer<? super T> action) {
if(value ! =null) { action.accept(value); }}Copy the code
This method accepts a consumptive function. If the value in the Optional instance is not empty, the Consumer accept method is called to consume the value; if it is empty, no processing is done. The above example can be rewritten using ifPresent:
Optional<String> optional = Optional.of("is present");
optional.isPresent((val) -> System.out.println("the value is " + val));
Copy the code
orElse
We can also read the value in Optional using the orElse method.
public T orElse(T other) {
returnvalue ! =null ? value : other;
}
Copy the code
This way you can define a default value that will be returned by the method if the Optional variable is null.
String optGet = null;
String orElse = Optional.ofNullable(optGet).orElse("Default");
/** * The following output is displayed: * Default */
Copy the code
orElseGet
If this method is not enough, we can use another method orElseGet:
public T orElseGet(Supplier<? extends T> supplier) {
returnvalue ! =null ? value : supplier.get();
}
Copy the code
This method differs from orElse in that the method implementing the Supplier interface or a Lambda expression is called to return the default value if the value is not present.
String optGet = null;
String orElse = Optional.ofNullable(optGet).orElse(() -> "Default");
/** * The following output is displayed: * Default */
Copy the code
orElseThrow
The orElseThrow method returns the value if it has a value and throws an exception created by the Supplier if it has no value. Let’s see how it works:
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if(value ! =null) {
return value;
} else {
throwexceptionSupplier.get(); }}Copy the code
In the orElseThrow principle, a Lambda expression or method is passed to throw an exception if the value does not exist:
class NoValueException extends RuntimeException {
public NoValueException(a) {
super(a); }@Override
public String getMessage(a) {
return "No value present in the Optional instance"; }}public static Integer orElseThrow(a) {
return (Integer) Optional.empty().orElseThrow(NoValueException::new);
}
public static void main(String[] args) {
orElseThrow();
}
* Exception in thread "main" xx.NoValueException: No value present in the Optional instance */
Copy the code
The difference between orElseThrow and orElseGet is that one throws an exception when there is no value, and one uses a Lambda expression to implement a default value when there is no value.
OrElseThrow only throws an exception when there is no value. What about methods that themselves throw an exception?
Now, let’s take integer.parseInt (String) as an example:
public static Integer toInt(String s) {
try {
// If the String can be converted to the corresponding Integer, return it wrapped in an Optional object
return Integer.parseInt(s);
} catch (NumberFormatException e) {
return null; // Return null or throw an exception}}Copy the code
When converting a String to an int, this method throws a NumberFormatException if the corresponding integer cannot be resolved. We catch the exception in this method using a try/catch statement, and cannot use an if condition to control whether a variable’s value is null.
In this case, we can use the Optional class to model invalid values returned by unconverted strings, so we can modify the above method:
public static Optional<Integer> toInt(String s) {
try {
// If the String can be converted to the corresponding Integer, return it wrapped in an Optional object
return Optional.of(Integer.parseInt(s));
} catch (NumberFormatException e) {
return Optional.empty(); // Otherwise return an empty Optional object}}Copy the code
This method of returning Optional works for many methods, so we just need to get an instance of the value wrapped by Optional.
map
Optional provides a map method to extract information from an object. It works as follows:
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if(! isPresent())return empty();
else {
returnOptional.ofNullable(mapper.apply(value)); }}Copy the code
The map operation applies the provided function to each element of the flow. We can think of an Optional object as a special kind of collection data that contains at most one element. If Optional contains a value, the value is converted using a Lambda expression that implements the Function interface. If you are not familiar with the Function interface, consult this article. The map method is as follows:
class Car {
private String name;
privateString type; . Omit getters and setters... } Optional<Car> optional = Optional.ofNullable(car); Optional<String> name = optional.map(Car::getName);Copy the code
flatMap
We can use the map method to get the Car name from the Person class wrapped by the Optional class:
class Person {
private Optional<Car> car;
public Person(Car car) {
this.car = Optional.of(car); }... Omit getters and setters... } Person person =new Person(new Car());
Optional<Person> optPerson = Optional.of(person);
Optional<String> name = optPerson.map(Person::getCar)
.map(Car::getName);
Copy the code
Unfortunately, this code does not compile. Why is that? OptPerson is a variable of type Optional
object, which means that the result of the map operation is an Optional
> object. Therefore, its call to getName is illegal because the outermost Optional object contains the value of another Optional object, which of course does not support the getName method.
So, we use the flatMap method. This method takes as an argument a function whose return value is another stream.
public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper) {
Objects.requireNonNull(mapper);
if(! isPresent()) {return empty();
} else {
@SuppressWarnings("unchecked")
Optional<U> r = (Optional<U>) mapper.apply(value);
returnObjects.requireNonNull(r); }}Copy the code
Rewrite the above example using flatMap with reference to the map function:
Optional<String> name = optPerson.flatMap(Person::getCar).map(Car::getName);
Copy the code
filter
Sometimes we need to filter the Optional value to get the result we want. We can use the filter method:
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if(! isPresent())return this;
else
return predicate.test(value) ? this : empty();
}
Copy the code
This method accepts the Predicate Predicate as an argument. If the value of the Optional object exists and the predicate condition is true, the filter method does nothing and returns the value. Otherwise, the value is filtered out and an empty Optional object is returned.
Optional<String> optionalS = Optional.of("13846901234");
optionalS = optionalS.filter(s -> s.contains("138"));
/** * The above 'filter' method can return the same Optional, otherwise return empty Optional */
Copy the code
conclusion
The introduction of Java.util.Optional
in Java 8 lets us handle null functionally, preventing null pointer exceptions; And supports multiple ways to manipulate values, such as: Map, flatMap, and filter, which can discard nested if-else code blocks, design better apis, and greatly improve code readability. However, if Optional is used in the domain model, it cannot be instantiated because the Serializable interface is not implemented. Nor can it be a field of a class.
For more information, please pay attention to the public account “Hai Ren Ji” and reply to “Resources” to get free learning resources!