Optional之第三篇总结
1、简介
Optional 是一个对象容器,具有以下两个特点:
提示用户要注意该对象有可能为null
简化if else代码
2、使用介绍
-
创建:
Optional.empty(): 创建一个空的 Optional 实例
Optional.of(T t):创建一个 Optional 实例,当 t为null时抛出异常
Optional.ofNullable(T t):创建一个 Optional 实例,但当 t为null时不会抛出异常,而是返回一个空的实例
-
获取:
get():获取optional实例中的对象,当optional 容器为空时报错
-
判断:
isPresent():判断optional是否为空,如果空则返回false,否则返回true
ifPresent(Consumer c):如果optional不为空,则将optional中的对象传给Comsumer函数
orElse(T other):如果optional不为空,则返回optional中的对象;如果为null,则返回 other 这个默认值
orElseGet(Supplier
other) :如果optional不为空,则返回optional中的对象;如果为null,则使用Supplier函数生成默认值otherorElseThrow(Supplier
exception) :如果optional不为空,则返回optional中的对象;如果为null,则抛出Supplier函数生成的异常 -
过滤:
filter(Predicate
p) :如果optional不为空,则执行断言函数p,如果p的结果为true,则返回原本的optional,否则返回空的optional -
映射:
map(Function
mapper) :如果optional不为空,则将optional中的对象 t 映射成另外一个对象 u,并将 u 存放到一个新的optional容器中。flatMap(Function< T,Optional> mapper):跟上面一样,在optional不为空的情况下,将对象t映射成另外一个optional
区别:map会自动将u放到optional中,而flatMap则需要手动给u创建一个optional
3、分析
3.1、创建源码:
// Optional的空实例对象,可以看到此时的value是为null的。但是这个Optional对象是不为null的,只是其中的属性value是为null的
private static final Optional<?> EMPTY = new Optional<>();
private final T value;
// 私有构造,不允许外界创建Optional对象
private Optional() {
this.value = null;
}
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
// 创建一个Optional空实例对象
public static Optional empty() {
@SuppressWarnings("unchecked")
Optional t = (Optional) EMPTY;
return t;
}
// 静态方法创建实例。如果value为null,那么将会爆出空指针异常;不为空的时候,才会返回一个value不为null的Optional对象
public static Optional of(T value) {
return new Optional<>(value);
}
// 如果value为null,那么返回的是一个Optional对象,但是其value属性是为null,但是Optional对象不为null
// 如果value不为null,那么返回的是一个Optional对象,其value实不为空的,就是传进来的value值。所以推荐使用的是这个
public static Optional ofNullable(T value) {
return value == null ? empty() : of(value);
}
创建源码小结:
创建对象的时候,因为构造方法是用private修饰的,所以无法在外界来进行构造对象。是能够利用当前类中的方法来进行调用。
使用empty方法,创建出来的是Optional对象,但是其中的value是null;
使用of方法,但是前提是value不能够为null;
使用ofNuallable方法,此时根据传入进来的value值分为两种情况:1、value不为空,那么创建出来的Optional对象的value不为null(有参构造会进行检查);2、value为null,会创建出来一个Optional对象,但是其属性value是为null的。
3.2、获取源码
public T get() {
// 如果获取的时候是为空的,那么报错
if (value == null) {
throw new NoSuchElementException("No value present");
}
// 否则返回Optional对象的value值
return value;
}
小结:这个方法就很鸡肋了,但是使用的时候,可以配置isPresent方法,先进行判断是否有值,有值的时候在进行get,没有值的时候就不来执行这个方法。
3.3、判断源码
是对当前的value值进行判断
isPresent()
// 如果Optional对象的属性value是不为null的,那么直接返回true;否则返回false
public boolean isPresent() {
return value != null;
}
ifPresent(Consumer c)
// 参数是lambda表达式,表示的是要对当前传递进来的匿名对象要进行什么样子的操作
public void ifPresent(Consumer<? super T> consumer) {
// 如果当前Optional对象的value属性不为null,那么就进行消费,消费方式开发者自己来进行定义
if (value != null)
consumer.accept(value);
}
3.4、默认方法
orElse(T other)
// 如果当前Optional对象的value属性是不为null的,那么直接返回当前Optional对象;如果value是为null的,那么将value赋值一个指定的属性
public T orElse(T other) {
return value != null ? value : other;
}
orElseGet(Supplier<? extends T> other)
public T orElseGet(Supplier<? extends T> other) {
// 当前value不为null,直接返回当前value值,否则给当前value赋值一个指定值
return value != null ? value : other.get();
}
orElseThrow(Supplier<? extends X> exceptionSupplier):
public T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
// 如果当前Optional对象的value不为null,那么直接返回这个值;如果为null,那么爆出指定的异常
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
3.5、过滤方法
filter(Predicate<? super T> predicate)
public Optional filter(Predicate<? super T> predicate) {
// 首先检查传递进来的匿名是否是为空的
Objects.requireNonNull(predicate);
// 首先判断当前Optional对象的value是否是为null的,如果为null,那么isPresent为fasle,!isPresent返回的是true,那么直接返回Optional对象
// 如果optional对象的value值是为null,那么这里就直接返回,不为null,那么就会走下面的方法
if (!isPresent())
return this;
else
// 对这里的value值来进行断言:这里的断言是自己来进行定义的:如果返回的是true,那么返回当前对象,为false的时候,返回的是一个Optional对象
// 其value属性是为null的
return predicate.test(value) ? this : empty();
}
那么从这里可以看到,这里的过滤方法只是说做一个判断而已,如果Optional对象中的value属性为null的时候,过滤方法还是会将其进行返回了。进入到下一步的操作中去。但是在stream流中,filter方法却是将符合条件的留在流中,但是这里的filter操作相当于还是返回了,但是此时此刻,如果要想来进行使用的话,那么还是需要结合其他方法,如isPresent方法来进行使用
3.6、映射方法
map(Function<? super T, ? extends U> mapper)方法
public Optional map(Function<? super T, ? extends U> mapper) {
// 首先检查是否为null
Objects.requireNonNull(mapper);
// 如果optional对象中的value是为null,那么为false,!isPresent判断后为true,直接返Optional对象value属性为null
if (!isPresent())
return empty();
else {
// 如果不为null,那么就开始进行映射方法。这里返回的对象是optional对象,但是其value属性不一定是有值的。
return Optional.ofNullable(mapper.apply(value));
}
}
那么这里在进行多次map的时候,可能会有多个Optional对象其value是为null的情况,那么就直接走return empty()的步骤了。
flatMap(Function<? super T, Optional> mapper)
public Optional flatMap(Function<? super T, Optional> mapper) {
// 检查匿名对象
Objects.requireNonNull(mapper);
// 如果optional对象中的value不为null,那么将走else;否则走true
if (!isPresent())
return empty();
else {
// 消费方法之后,返回的是一个Optional对象
return Objects.requireNonNull(mapper.apply(value));
}
}
4、例子
4.1、Demo1-filter方法
需求:学校想从一批学生中,选出年龄大于等于18,参加过考试并且成绩大于80的人去参加比赛。
@Test
public void testOptionalOne() {
Student s1 = new Student("张三", 19, 80);
Student s2 = new Student("李四", 19, 50);
Student s3 = new Student("王五", 23, null);
Student s4 = new Student("赵六", 16, 90);
Student s5 = new Student("钱七", 18, 99);
Student s6 = new Student("孙八", 20, 40);
Student s7 = new Student("吴九", 21, 88);
List students = Arrays.asList(s1, s2, s3, s4, s5, s6, s7);
List collect = students.stream()
.filter(student -> Optional.ofNullable(student.getAge()).filter(age -> age >= 18).isPresent())
.filter(student -> Optional.ofNullable(student.getScore()).filter(score -> score > 80).isPresent()).collect(Collectors.toList());
collect.forEach(System.out::println);
}
注意到这段代码:
Optional.ofNullable(student.getAge()).filter(age -> age >= 18).isPresent()
如果不加后面的isPresent方法,那么返回的是可能是两种结果:optional对象的value属性有value=null,也有value!=null的,但是这里很明显,需要过滤掉value=null的情况。结合着其他情况来进行使用。
4.2、Demo2-orElse-orElseGet
测试一下orElse和orElseGet方法,这里会有一个很有意思的问题:
定义一个User类:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String email;
private String id;
}
然后写两个测试:
测试一:
private User createNewUser() {
log.info("Creating New User");
return new User("extra@gmail.com", "1234555");
}
@Test
public void testOrElseAndOrElseGetOne() {
User user = null;
log.info("Using orElse");
User result = Optional.ofNullable(user).orElse(createNewUser());
log.info("Using orElseGet");
User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
}
如果user为null,那么看下orElse和orElseGet方式取值:
2021-11-20 11:18:21.031 INFO 17328 --- [ main] c.e.s.SpringbootMvcDemo1ApplicationTests : Using orElse
2021-11-20 11:18:21.031 INFO 17328 --- [ main] c.e.s.SpringbootMvcDemo1ApplicationTests : Creating New User
2021-11-20 11:18:21.031 INFO 17328 --- [ main] c.e.s.SpringbootMvcDemo1ApplicationTests : Using orElseGet
2021-11-20 11:18:21.031 INFO 17328 --- [ main] c.e.s.SpringbootMvcDemo1ApplicationTests : Creating New User
可以看到,user为空的时候,两个都会走createNewUser方法。
测试二:
private User createNewUser() {
log.info("Creating New User");
return new User("extra@gmail.com", "1234555");
}
@Test
public void testOrElseAndOrElseGetTwo() {
User user = new User("john@gmail.com", "1234");
log.info("Using orElse");
User result = Optional.ofNullable(user).orElse(createNewUser());
log.info("Using orElseGet");
User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
}
当user不为空的时候,那么继续来进行测试:
2021-11-20 11:20:09.274 INFO 29156 --- [ main] c.e.s.SpringbootMvcDemo1ApplicationTests : Using orElse
2021-11-20 11:20:09.274 INFO 29156 --- [ main] c.e.s.SpringbootMvcDemo1ApplicationTests : Creating New User
2021-11-20 11:20:09.274 INFO 29156 --- [ main] c.e.s.SpringbootMvcDemo1ApplicationTests : Using orElseGet
可以看到即使是user不为null的时候,orElse也会走后面的方法来创建对象,但是orElseGet方法不会。
所以说value属性无论是null,orElse后面的方法都会执行;而对于orElseGet来说,满足了条件就不会走;
那么如果对于执行较为密集的情况下,如果使用了orElse方法,会降低性能;所以为了方便起见,推荐使用orElseGet方法。
我看了一下两个方法源码:
public T orElse(T other) {
return value != null ? value : other;
}
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
我在这里有一个疑问:方法进栈了之后就会开始执行对应的方法,那么为什么第一个执行了,第二个没有执行??
如果orElse方法中只是固定值,那么可以直接写,但是如果是方法的话,那么建议慎用。
4.3、Demo3-orElseThrow
需求:如果用户的email邮箱为空,那么抛出异常
首先自定义一个运行时异常:
public class BussinussException extends RuntimeException{
public BussinussException() {
}
public BussinussException(String message) {
super(message);
}
}
测试代码:
@Test
public void testOrElseThrow(){
User user = null;
String email = Optional.ofNullable(user).map(user1 -> user.getEmail()).orElseThrow(() -> new BussinussException("email为空"));
log.info("email的值是:{}",email);
}
控制台:
com.example.springbootmvcdemo1.BussinussException: email为空
这个在做参数校验的时候,也是很好来进行使用的。
4.4、Demo4-map和flatMap
测试map和flatMap
测试一:
需求:判断下面代码的执行顺序
Optional.ofNullable(getYes())
.map(a -> a.getAddress())
.map(p -> p.getProvince())
.map(n -> n.getName())
.orElseThrow(NoSuchElementException::new);
这里其实只需要根据map的映射方法的源码就可以知道。
public Optional map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
// value为null的时候,直接返回optional对象,但是其中的value是为null的
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value));
}
}
所以上面的代码中应该是每个都会执行,只不过执行到了orElseThrow的时候:
public T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
在这里发现了value是为null的,那么将会报出异常。
测试二:
public class User {
private Address address;
public Optional getAddress() {
return Optional.ofNullable(address);
}
// ...
}
public class Address {
private Country country;
public Optional getCountry() {
return Optional.ofNullable(country);
}
// ...
}
测试代码:
@Test
public void whenChaining_thenOk() {
User user = new User("anna@gmail.com", "1234");
String result = Optional.ofNullable(user)
.flatMap(u -> u.getAddress())
.flatMap(a -> a.getCountry())
.map(c -> c.getIsocode())
.orElse("default");
assertEquals(result, "default");
}
那么看一下flatMap源码:
public Optional flatMap(Function<? super T, Optional> mapper) {
Objects.requireNonNull(mapper);
// 如果optional对象中得value为null,那么就直接返回空对象
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));
}
}
上面的操作中的转换步骤是:Optional
但是如果返回的不是Optional类型的,那么就不需要用到这个方法了。
5、总结
可以看到Optional类中的方法,对于map、filter等方法,返回的都是一个Optional对象,但是都是包裹起来的,无论value是有值的还是为null的,所以这里可以很方便我们来进行下一步操作。上面这句话很重要