写点什么

Java8 新特性 -Optional

  • 2022 年 4 月 28 日
  • 本文字数:3132 字

    阅读完需:约 10 分钟

使用案例


@Test


public void testIsPresent() {


Optional<String> optional = Optional.of("thinkwon");


Optional<String> optional1 = Optional.ofNullable(null);


System.out.println(optional.isPresent());


System.out.println(optional1.isPresent());


}


输出结果


true


false


注意:下面例子的用法不但没有减少 null 的防御性检查,而且增加了 Optional 包装的过程,违背了 Optional 设计的初衷,因此开发中要避免这种糟糕的使用


试想一下如果先用 isPresent 方法获得是否存在,然后决定是否调用 get 方法和之前的 ifelse 判断并无二致。


@Test


public void testIsPresent1() {


Optional<String> optional = Optional.ofNullable(null);


if (optional.isPresent()) {


System.out.println(optional.get());


}


}

[](()ifPresent(Consumer<? super T> consumer)方法

ifPresent()方法接受一个 Consumer 对象(消费函数),如果包装对象的值非空,运行 Consumer 对象的 accept()方法


public void ifPresent(Consumer<? super T> consumer) {


if (value != null)


consumer.accept(value);


}


使用案例


@Test


public void testIfPresent() {


Optional<String> optional = Optional.of("thinkwon");


optional.ifPresent(s -> System.out.println("the String is " + s));


}


输出结果


the String is thinkwon

[](()filter()方法

filter()方法接受参数为 Predicate 对象,用于对 Optional 对象进行过滤,如果符合 Predicate 的条件,返回 Optional 对象本身,否则返回一个空的 Optional 对象。


public Optional<T> filter(Predicate<? super T> predicate) {


Objects.requireNonNull(predicate);


if (!isPresent()) {


return this;


} else {


return predicate.test(value) ? this : empty();


}


}


使用案例


@Test


public void testFilter() {


Optional.of("thinkwon").filter(s -> s.length() > 2)


.ifPresent(s -> System.out.println("The length of String is greater than 2 and String is " + s));


}


输出结果


The length of String is greater than 2 and String is thinkwon

[](()map()方法

map()方法的参数为 Function(函数式接口)对象,map()方法将 Optional 中的包装对象用 Function 函数进行运算,并包装成新的 Optional 对象(包装对象的类型可能改变)


public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {


Objects.requireNonNull(mapper);


if (!isPresent()) {


return empty();


} else {


return Optional.ofNullable(mapper.apply(value));


}


}


使用案例


@Test


public void testMap() {


Optional<String> optional = Optional.of("thinkwon").map(s -> s.toUpperCase());


System.out.println(optional.get());


}


输出结果


THINKWON

[](()flatMap()方法

跟 map()方法不同的是,入参 Function 函数的返回值类型为Optional<U>类型,而不是 U 类型,这样 flatMap()能将一个二维的 Optional 对象映射成一个一维的对象。


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>) 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 mapper.apply(value);


return Objects.requireNonNull(r);


}


}


使用案例


@Test


public void testFlatMap() {


Optional<String> optional = Optional.of("thinkwon").flatMap(s -> Optional.ofNullable(s.toUpperCase()));


System.out.println(optional.get());


}


输出结果


THINKWON

[](()orElse()方法

orElse()方法功能比较简单,即如果包装对象值非空,返回包装对象值,否则返回入参 other 的值(默认值)


public T orElse(T other) {


return value != null ? value : other;


}


使用案例


@Test


public void testOrElse() {


String unkown = (String) Optional.ofNullable(null).orElse("unkown");


System.out.println(unkown);


}


输出结果


unkown

[](()orElseGet()方法

orElseGet()方法与 orElse()方法类似,区别在于 orElseGet()方法的入参为一个 Supplier 对象,用 Supplier 对象的 get()方法的返回值作为默认值


public T orElseGet(Supplier<? extends T> supplier) {


return value != null ? value : supplier.get();


}


使用案例


@Test


public void testOrElseGet() {


String unkown = (String) Optional.ofNullable(null).orElseGet(() -> "unkown");


System.out.println(unkown);


}


输出结果


unkown

[](()orElseThrow()方法

orElseThrow()方法其实与 orElseGet()方法非常相似了,入参都是 Supplier 对象,只不过 orElseThrow()的 Supplier 对象必须返回一个 Throwable 异常,并在 orElseThrow()中将异常抛出,orElseThrow()方法适用于包装对象值为空时需要抛出特定异常的场景。


public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {


if (value != null) {


return value;


} else {


throw exceptionSupplier.get();


}


}


使用案例


@Test


public void testOrElseThrow() {


Optional.ofNullable(null).orElseThrow(() -> new RuntimeException("unkown"));


}


输出结果


java.lang.RuntimeException: unkown


[](()Optional 实战




下面展示一下Optional的一些常见的使用场景。

[](()空判断

空判断主要是用于不知道当前对象是否为NULL的时候,需要设置对象的属性。不使用Optional时候的代码如下:


if(null != order){


order.setAmount(orderInfoVo.getAmount());


}


使用Optional时候的代码如下:


Optional.ofNullable(order).ifPresent(o -> o.setAmount(orderInfoVo.getAmount()));


使用Optional实现空判断的好处是只有一个属性设值的时候可以压缩代码为一行,这样做的话,代码会相对简洁。

[](()断言

在维护一些老旧的系统的时候,很多情况下外部的传参没有做空判断,因此需要写一些断言代码如:


if (null == orderInfoVo.getAmount()){


throw new IllegalArgumentException(String.format("%s 订单的 amount 不能为 NULL",orderInfoVo.getOrderId()));


}


if (StringUtils.isBlank(orderInfoVo.getAddress()){


throw new IllegalArgumentException(String.format("%s 订单的 address 不能为空",orderInfoVo.getOrderId()));


}


使用Optional后的断言代码如下:


Optional.ofNullable(orderInfoVo.getAmount()).orElseThrow(()-> new IllegalArgumentException(String.format("%s 订单的 amount 不能为 NULL",orderInfoVo.getOrderId())));


Optional.ofNullable(orderInfoVo.getAddress()).orElseThrow(()-> new IllegalArgumentException(String.format("%s 订单的 address 不能为空",orderInfoVo.getOrderId())));


[](()使用误区




关于使用 Optional 的误区有以下:


  • 正确的使用创建方法,不确定是否为 null 时尽量选择 ofNullable 方法;

  • 通过源代码会发现,它并没有实现 java.io.Serializable 接口,因此应避免在类属性中使用,防止意想不到的问题;

  • 避免直接调用 Optional 对象的 get 和 isPresent 方法,尽量多使用 map()、filter()、orElse()等方法来发挥 Optional 的作用。


[](()总结




Optional本质是一个对象容器,它的特征如下:


  1. Optional作为一个容器承载对象,提供方法适配部分函数式接口,结合部分函数式接口提供方法实现NULL判断、过滤操作、安全取值、映射操作等等。

  2. Optional一般使用场景是用于方法返回值的包装,当然也可以作为临时变量从而享受函数式接口的便捷功能。

  3. Optional只是一个简化操作的工具,可以解决多层嵌套代码的节点空判断问题(例如简化箭头型代码)。

  4. Optional并非银弹。


这里提到箭头型代码,下面尝试用常规方法和Optional分别解决:


// 假设 VO 有多个层级,每个层级都不知道父节点是否为 NULL,如下


// - OrderInfoVo

用户头像

还未添加个人签名 2022.04.13 加入

还未添加个人简介

评论

发布
暂无评论
Java8新特性-Optional_Java_爱好编程进阶_InfoQ写作社区