【java基础】一篇文章彻底搞懂Optional
文章目录
- 基本说明
- Optional类介绍
- Optional的创建
- 获取Optional的值
-
- get方法
- orElse方法
- orElseGet方法
- orElseThrow
- 消费Optional值
-
- ifPresent方法
- ifPresentOrElse
- 管道化Optional值
-
- map方法
- filter方法
- or方法
- flatMap的用法
- 测试代码
- 使用Optional的一些注意事项
- 总结
基本说明
Optional对象是一种包装器对象,要么包装了类型T的对象,要么没有包装任何对象。对于第一种情况,我们称这种值是存在的。Optional类型被当作一种更安全的方式,用来替代类型T的引用,这种引用要么引用某个对象,要么为null。但是,它只有在正确使用的情况下才会更安全,在这篇文章中会进行介绍
注意:学习Optional,必须得要熟练掌握泛型和lambda表达式,前置知识还有Stream。如果不清楚,请参考泛型请参考 泛型程序设计基础 ,lambda请参考 一篇文章彻底搞懂lambda表达式,Stream请参考Stream流的各种操作
Optional类介绍
在java中,我们要学习一个类,第一步要做的应该就是去了解该类的继承关系以及有些上面方法,在有需要时还应当查看一些源代码中的注释。
下面我们就来看看Optional的类图以及该类中的方法和字段
我们再来看看该类上面的注释
A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.
Additional methods that depend on the presence or absence of a contained value are provided, such as orElse() (return a default value if value not present) and ifPresent() (execute a block of code if the value is present).
This is a value-based class; use of identity-sensitive operations (including reference equality (==), identity hash code, or synchronization) on instances of Optional may have unpredictable results and should be avoided.
翻译过来就是
一个容器对象,它可能包含也可能不包含非null值。如果存在一个值,isPresent()将返回true,get()则返回该值。提供了取决于所包含值是否存在的其他方法,如orElse()(如果值不存在,则返回默认值)和ifPresent()(当值存在时执行代码块)。这是一个基于价值的类;在Optional实例上使用标识敏感操作(包括引用相等(==)、标识哈希代码或同步)可能会产生不可预测的结果,应避免。
Optional的创建
我们要创建Optional是通过该类的静态方法of或者ofNullable方法,通过empty方法可以产生一个为空的Optional
of方法创建
// of方法中不能传入null,如果传入null,会提示空指针异常Optional<String> stringOptional = Optional.of("hello world");// NullPointerExceptionOptional<Object> nullOption = Optional.of(null);
ofNullable方法创建
// 和of方法一样的,只不过当传入的值为null时返回一个空的OptionalOptional<String> stringOptional = Optional.ofNullable("hello");Optional<Object> nullOption = Optional.ofNullable(null);
empty方法创建
Optional<Object> empty = Optional.empty();
获取Optional的值
这里介绍四个方法来获取Optional的值,分别是get,orElse,orElseGet,orElseThrow。它们都能获取Optional对象的值,区别就是Optional对象为空时的操作不一样
get方法
该方法可以获取Optional的值
Optional<String> stringOptional = Optional.of("hello");Optional<Object> empty = Optional.empty();String s = stringOptional.get();System.out.println(s);Object o = empty.get();System.out.println(o);
上面代码的输出如下
当使用get获取为空的Optional时会报一个NoSuchElementException
orElse方法
该方法和get一样,只不过当Optional为空时会返回一个默认值
Optional<String> stringOptional = Optional.of("hello");Optional<Object> empty = Optional.empty();String s = stringOptional.orElse("自己定义的默认值1");System.out.println(s);Object o = empty.orElse("自己定义的默认值2");System.out.println(o);
上面代码输出为
orElseGet方法
这个方法会在Optional为空时调用传入的lambda生成一个值
如果大家忘记了Supplier,请参考下面的源代码
Optional<String> stringOptional = Optional.of("hello");Optional<Object> empty = Optional.empty();Supplier<String> supplier = () -> {System.out.println("\\n----执行lambda----");return "default Value";};String s = stringOptional.orElseGet(supplier);System.out.println(s);Object o = empty.orElseGet(supplier);System.out.println(o);
上面的代码输出为
orElseThrow
经过上面三个方法的学习想必这个方法做什么的大家都能够猜出来了,就是当Optional为空时抛出一个自己传入的异常。注意:传入的是lambda表达式
Optional<String> stringOptional = Optional.of("hello");Optional<Object> empty = Optional.empty();String s = stringOptional.orElseThrow(ArrayIndexOutOfBoundsException::new);System.out.println(s);Object o = empty.orElseThrow(ArithmeticException::new);System.out.println(o);
上面代码输出为
消费Optional值
在上一小节,我们看到了如何在不存在任何值的情况下产生相应的替代物。另一条使用可选值的策略是只有在其存在的情况下才消费该值。
ifPresent方法
ifPresent方法会接受一个函数。如果可选值存在,那么它会被传递给该函数。否则,不会发生任何事情。
我们需要传入一个Consumer,下面就是Consumer的源代码
Optional<String> stringOptional = Optional.of("hello");Optional<Object> empty = Optional.empty();stringOptional.ifPresent(x -> System.out.println(1));empty.ifPresent(x -> System.out.println(2));
上面代码输出为
ifPresentOrElse
如果想要在可选值存在时执行一种动作,在可选值不存在时执行另一种动作,可以使用ifPresentOrElse。注意:该方法是java9新增的,java8不能使用
Optional<String> stringOptional = Optional.of("hello");Optional<Object> empty = Optional.empty();// jdk 9stringOptional.ifPresentOrElse(System.out::println, () -> System.out.println("默认输出1"));stringOptional.ifPresentOrElse(System.out::println, () -> System.out.println("默认输出2"));
上面代码输出为
管道化Optional值
对于管道化Optional值,我们就直接简单理解Optional就是大小为0或者1得流,既然流里面有map,filter,当然在Optional中也可以进行相同的操作
map方法
map方法可以用来转换Optional内部的值
我们查看源代码,其实该方法的本质就是调用了ofNullable方法。Function接口内容如下
Optional<String> stringOptional = Optional.of("hello");Optional<Object> empty = Optional.empty();Optional<String> newStrOptional = stringOptional.map(v -> "hello world");System.out.println(newStrOptional);Optional<String> newEmptyOptional = empty.map(v -> "hello world xxx");System.out.println(newEmptyOptional);
上面代码输出为
filter方法
这个方法听名字就知道是过滤的,使用该方法,如果满足条件或者为空,那么就返回当前对象本身,否则返回一个空Optional
Predicate接口内容如下
Optional<String> stringOptional = Optional.of("hello");Optional<String> stringOptional1 = stringOptional.filter(v -> v.length() > 3);Optional<String> stringOptional2 = stringOptional.filter(v -> v.length() > 6);System.out.println(stringOptional1);System.out.println(stringOptional2);
上面代码输出为
or方法
调用这个方法,在Optional为空时会根据传入的lambda表达式生成一个Optional方法,注意:该方法需要jdk9+
Optional<Object> empty = Optional.empty();// jdk9Optional<Object> or = empty.or(()->Optional.of("xxx"));System.out.println(empty);System.out.println(or);
上面代码输出为
flatMap的用法
大家如果学过Stream里面的flatMap,那么再来学习Optional里面的flatMap应该就容易很多了。
Stream里面的flatMap是将产生流的两个方法组合起来,其实现方式是摊平由流构成的流。如果将可选值解释为具有0个或1个元素,那么Optional.flatMap方法与其操作方式一样。
flatMap的源代码如下
现在假设你有一个可以产生Optional<T>对象的方法f,并且目标类型T具有一个可以产生Optional<T>对象的方法g。如果它们都是普通的方法,那么你可以通过调用s.f().g()来将它们组合起来。但是这种组合无法工作,因为s.f()的类型为Optional<T>,而不是T。因此,需要调用flatMap。
下面代码就是一个示例
class B {}class A {public Optional<A> f() {return Optional.of(new A());}public Optional<B> g() {return Optional.of(new B());}}A a = new A();// errora.f().g();// okOptional<B> optionalB = a.f().flatMap(A::g);
如果s.f()的值存在,那么g就可以应用到它上面。否则,就会返回一个空Optional<T>。很明显,如果有更多可以产生Optional值的方法或lambda表达式,那么就可以重复此过程。你可以直接将flatMap的调用链接起来,从而构建由这些步骤构成的管道,只有所有步骤都成功,该管道才会成功。
测试代码
下面就是本篇文章中所使用的代码,大家感兴趣可以自己运行测试一下
package com.ttpfx;import org.junit.Test;import java.util.Optional;
import java.util.function.Supplier;/* @author ttpfx* @date 2023/3/20*/
public class OptionTest {public static void main(String[] args) {}@Testpublic void of() {// of方法中不能传入null,如果传入null,会提示空指针异常Optional<String> stringOptional = Optional.of("hello world");// NullPointerExceptionOptional<Object> nullOption = Optional.of(null);}@Testpublic void ofNullable() {// 和of方法一样的,只不过当传入的值为null时返回一个空的OptionalOptional<String> stringOptional = Optional.ofNullable("hello");Optional<Object> nullOption = Optional.ofNullable(null);}@Testpublic void empty() {Optional<Object> empty = Optional.empty();}@Testpublic void get() {Optional<String> stringOptional = Optional.of("hello");Optional<Object> empty = Optional.empty();String s = stringOptional.get();System.out.println(s);Object o = empty.get();System.out.println(o);}@Testpublic void orElse() {Optional<String> stringOptional = Optional.of("hello");Optional<Object> empty = Optional.empty();String s = stringOptional.orElse("自己定义的默认值1");System.out.println(s);Object o = empty.orElse("自己定义的默认值2");System.out.println(o);}@Testpublic void orElseGet() {Optional<String> stringOptional = Optional.of("hello");Optional<Object> empty = Optional.empty();Supplier<String> supplier = () -> {System.out.println("\\n----执行lambda----");return "default Value";};String s = stringOptional.orElseGet(supplier);System.out.println(s);Object o = empty.orElseGet(supplier);System.out.println(o);}@Testpublic void orElseThrow() {Optional<String> stringOptional = Optional.of("hello");Optional<Object> empty = Optional.empty();String s = stringOptional.orElseThrow(ArrayIndexOutOfBoundsException::new);System.out.println(s);Object o = empty.orElseThrow(ArithmeticException::new);System.out.println(o);}@Testpublic void ifPresent() {Optional<String> stringOptional = Optional.of("hello");Optional<Object> empty = Optional.empty();stringOptional.ifPresent(x -> System.out.println(1));empty.ifPresent(x -> System.out.println(2));}// @Test// public void ifPresentOrElse() {// Optional<String> stringOptional = Optional.of("hello");// Optional<Object> empty = Optional.empty();//// // jdk 9// stringOptional.ifPresentOrElse(System.out::println, () -> System.out.println("默认输出1"));// empty.ifPresentOrElse(System.out::println, () -> System.out.println("默认输出2"));// }@Testpublic void map() {Optional<String> stringOptional = Optional.of("hello");Optional<Object> empty = Optional.empty();Optional<String> newStrOptional = stringOptional.map(v -> "hello world");System.out.println(newStrOptional);Optional<String> newEmptyOptional = empty.map(v -> "hello world xxx");System.out.println(newEmptyOptional);}@Testpublic void filter() {Optional<String> stringOptional = Optional.of("hello");Optional<String> stringOptional1 = stringOptional.filter(v -> v.length() > 3);Optional<String> stringOptional2 = stringOptional.filter(v -> v.length() > 6);System.out.println(stringOptional1);System.out.println(stringOptional2);}// @Test// public void or() {// Optional<Object> empty = Optional.empty();// // jdk9// Optional<Object> or = empty.or(()->Optional.of("xxx"));//// System.out.println(empty);// System.out.println(or);// }@Testpublic void flatMap() {class B {}class A {public Optional<A> f() {return Optional.of(new A());}public Optional<B> g() {return Optional.of(new B());}}A a = new A();// error// a.f().g();// okOptional<B> optionalB = a.f().flatMap(A::g);}@Testpublic void optionalToStream() {Optional<String> stringOptional = Optional.of("hello");}
}
使用Optional的一些注意事项
下面是一些有关Optional类型正确用法的提示:
- Optional类型的变量永远都不应该为null。
- 不要使用Optional类型的域。因为其代价是额外多出来一个对象。在类的内部,使用null表示缺失的域更易于操作。
- 不要在集合中放置Optional对象,并且不要将它们用作map的键。应该直接收集其中的值。
总结
对于Optional,我们简单理解为就是对某种类型的封装即可。要么为该对象本身,要么为null。