概述

Optional的本意是为了避免null带来的错误,为我们提供一个可以明确表示空值的有限的机制。

基础理解

Optional是一个容器,用于放置可能为空的值,它可以合理而优雅的处理null。
Jdk1.8后,jdk新增了Optional来表示空结果。其实本质上什么也没有变,只是增加了一个表达式。
Optional表示空的静态方法为Optional.empty(),跟null有什么本质区别吗?看它的实现,Optional中的value就是null,只不过包了一层Optional,所以说它其实是个容器。用了之后的代码可能如下:

// 1
Optional<User> optionalUser = RemoteService.getUser();
if (!optionalUser.isPresent()) {//doing something
}
User user = optionalUser.get();
// 2
User user = optionalUser.orElse(new User());

看起来,好像好了一点,至少看起来没那么笨了。但是如果采用写法1,好像更啰嗦了。

不合适的使用场景

直接使用isPresent()进行if检查

这个直接参考上面的例子,用if判断和1.8之前的写法并没有什么区别,反而返回值包了一层Optional,增加了代码的复杂性,没有带来任何实质性的收益。其实isPresent()一般用于流处理的结尾,用于判断是否符合条件。

boolean aaa = list.stream().filter(o -> Objects.equals(o, param)).findFirst().isPresent();

在方法参数中使用Optional

我们用一个东西之前得想明白,这东西是为解决什么问题而诞生的。
Optional直白点说就是为了表达可空性,如果方法参数可以为空,为何不重载呢?包括使用构造函数也一样。重载的业务表达更加清晰直观。

    public void getUser(String uuid, Optional<UserType> optional) {     }public void getUser(String uuid) {getUser(uuid,null);}public void getUser(String uuid, UserType userType) {// do something}

直接使用Optional.get

Optional不会帮你做任何的空判断或者异常处理,如果直接在代码中使用Optional.get()和不做任何判断一样,十分危险。这可能会出现在那种所谓的急着上线,着急交付,对Optional也不是很熟悉,直接就用了。所以这就要我们平时保持学习,都是信手拈来的东西。

使用在注入的属性中

这种写法估计用的人很少,但是不排除脑洞大开的。

public class CommonService {private Optional<UserService> userService;public User getUser(String name) {return userService.ifPresent(u -> u.findByName(name));}
}

首先依赖注入大多在spring的框架下,直接使用@Autowired很方便。但是如果使用以上的写法,如果userService设置失败了,程序就应该终止并报异常,并不是无声无息,让其看起来什么问题都没有。

Optional的API

empty()

返回一个Optional容器对象,而不是null。建议常用 ⭐⭐⭐⭐

// empty.get()值为null
Optional<Object> empty = Optional.empty();

of(T value)

创建一个Optional对象,如果value是null,则抛出NPE(null point exception)。不建议使用。

ofNullable(T value)

创建一个Optional对象,但是value为空时返回Optional.empty()。推荐使用⭐⭐⭐⭐⭐

get()

返回Optional中包装的值,在判空之前,千万不要直接使用!尽量别用。

orElse(T other)

同样返回Optional中包装的值,但不同的是当取不到值时,返回你指定的default。看似很好,但不建议使用⭐⭐

orElseGet(Supplier<? extends T> other)

同样是返回Optional中包装的值,取不到值是,返回你指定的default,看似和上面一样,但是推荐使用⭐⭐⭐⭐⭐

orElseThrow(Supplier<? extends X> exceptionSupplier)

返回Optional中包装的值,取不到时抛出指定的异常。阻塞性业务场景推荐使用⭐⭐⭐⭐

isPresent()

判断Optional中是否有值,返回boolean,某些情况下很好用,但尽量不用在if判断体中。可以用⭐⭐⭐

ifPresent(Consumer<? super T> consumer)

判断Optional中是否有值,有值则执行consumer,否则什么都不干。日常情况使用这样⭐⭐⭐⭐

提示

一些基本原则:

  • 不要声明任何Optional实例属性。
  • 不要再任何setter或者构造方法中使用Optional
  • Optional属于返回类型,在业务返回值或者远程远程调用中使用

最佳实践

对象可空,需要获取对象属性

Optional.ofNullable(resData).orElse(Collections.emptyMap()).get("data")

业务上需要空值时,不要直接返回null,使用Optional.empty()

    public Optional<String> getOptionResult(String user) {if (Objects.nonNull(user)) {return Optional.ofNullable(user);}return Optional.empty();}

使用orElseGet()

使用orElseGet()主要是从性能的角度考虑。
获取value有三种方式:get(),orElse(),orElseGet()。这里推荐在需要用的地方只有orElseGet()。
首先,get()不能直接使用,需要结合判空使用。这和!=null其实没有多大区别,只是在表达和抽象上有所改善。
其次,为什么不推荐orElse()呢?因为orElse()不论如何都会指向括号中内容,orElseGet()只在主体value是null时才执行:

    public String getResult() {System.out.println("call result");return "result";}public void testOrElseGet() {// out:Optional.ofNullable("result").orElseGet(() -> {System.out.println("orElseGet call exe");return "call";});// out: call resultOptional.ofNullable("result").orElse(getResult());}

如果上面的例子getResult()方法是一个远程调用,或者涉及到大量的文件IO,代价可想而知。
但orElse也是有用武之地的。orElseGet()需要构建一个Supplier函数接口对象,如果只是简单的返回一个静态资源、字符串等等,直接返回静态资源即可。

public static final String USER_STATUS = "UNKNOWN";
...
public String findUserStatus(long id) {Optional<String> status = ... ; // return status.orElse(USER_STATUS);
}//不要这么写
public String findUserStatus(long id) {Optional<String> status = ... ; // return status.orElse("UNKNOWN");//这样每次都会新建一个String对象
}

使用orElseThrow()

这个针对阻塞性的业务场景比较合适,例如没有从上游获取到用户信息,下面的所有操作都无法进行,那此时就应该抛出异常。正常的写法是先判空,再手动throw异常,现在可以集成为一行:

    public User testThrow(User user) throws Exception {return Optional.ofNullable(user).orElseThrow(Exception::new);}

不为null则执行时,使用ifPresent

这点没有性能上的优势,但可以使代码更简洁:

//之前是这样的
if (status.isPresent()) {System.out.println("Status: " + status.get());
}//现在
status.ifPresent(System.out::println);

不要滥用

有些简单明了的方法,完全没有必要增加Optional来增加复杂性。

public String fetchStatus() {String status = getStatus() ;return Optional.ofNullable(status).orElse("PENDING");
}//判断一个简单的状态而已
public String fetchStatus() {String status = ... ;return status == null ? "PENDING" : status;
}

使用map从Optional对象中提取和转换值

从对象中提取信息是一种比较常见的模式。比如,你可能想从insurance公司对象中提取公司的名称。提取名称之前,你需要检查insurance对象是否为null,代码如下:

        String name = null;if (insurance != null) {name = insurance.getName();}

为了支持这种模式,Optional提供了一个map方法。它的工作方式如下:

        final Optional<Insurance> optInsurance = Optional.ofNullable(insurance);final Optional<String> name = optInsurance.map(Insurance::getName);

从概念上,这与流的map方法相差无几。map操作会将提供的函数应用于流的每个元素。你可以把Optional对象看成一种特殊的集合数据,它至多包含一个元素。如果Optional包含一个值,那函数就将该值作为参数传递给map,对该值进行转换。如果Optional为空,就什么也不做。下图展示把一个正方形转换为三角形的函数,分别传递给正方形和Optional正方形流的map方法之后的结果。

使用flatMap链接Optional对象

可以把flatMap理解为打平 Optional<Optional>为Optional。
由于我们刚刚学习了如何使用map,你的第一反应可能是我们可以利用map重写之前的代码,如下所示:

    @Datastatic class Insurance {private String name;}@Datastatic class Car {private Optional<Insurance> insurance;}@Datastatic class Person {private Optional<Car> car;}final Optional<Person> optPerson = Optional.ofNullable(person);final Optional<String> name = optPerson.map(Person::getCar).map(Car::getInsurance).map(Insurance::getName);

不幸的是,这段代码无法通过编译。因为optPerson是Optional类型的变量,调用map方法没问题,但getCar返回的是一个Optional类型的对象,这意味着map操作的结果是一个Optional<Optional>类型的对象。因
此,它对 getInsurance 的调用是非法的,因为最外层的 optional 对象包含了另一个 optional
对象的值,而它当然不会支持 getInsurance 方法。

所以,我们该如何解决这个问题呢?让我们再回?一下你??在流上使用过的模式:
flatMap 方法。使用流时, flatMap 方法接受一个函数作为参数,这个函数的返回值是另一个流。
这个方法会应用到流中的每一个元素,最终形成一个新的流的流。但是 flagMap 会用流的内容替
换每个新生成的流。换句话说,由方法生成的各个流会被合并或者?平化为一个单一的流。这里
你希望的结果其实也是类似的,但是你想要的是将两层的 optional 合并为一个。

因此我们可以使用下面代码获取car的保险公司名称:

        final Optional<Person> optPerson = Optional.ofNullable(person);final String name = optPerson.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("Unknow");


截止目前为止,返回的Optional可能有两种情况:如果调用链上的任何一个方法返回一个空的Optional,那么结果就为空,否则返回的值就是你期望的保险公司的名称。

使用filter剔除特定的值

你经常需要调用某个对象的方法,查看它的某些属性。比如,你可能需要查保险公司的名
称是否为“Cambridge-Insurance”。为了以一种安全的方式进行这些操作,你首先需要确定引用指
向的 Insurance 对象是否为 null ,之后再调用它的 getName 方法,如下所示:

Insurance insurance = ...;
if(insurance != null && "CambridgeInsurance".equals(insurance.getName())){System.out.println("ok");
}

使用 Optional 对象的 filter 方法,这段代码可以重构如下:

Optional<Insurance> optInsurance = ...;
optInsurance.filter(insurance ->  "CambridgeInsurance".equals(insurance.getName())).ifPresent(x -> System.out.println("ok"));

filter 方法接受一个谓词作为参数。如果 Optional 对象的值存在,并且它符合谓词的条件,
filter 方法就返回其值;否则它就返回一个空的 Optional 对象。如果你还记得我们可以将
Optional 看成最多包含一个元素的 Stream 对象,这个方法的行为就非常清晰了。如果 Optional
对象为空,它不做任何操作,反之,它就对 Optional 对象中包含的值施加谓词操作。如果该操
作的结果为 true ,它不做任何改变,直接返回该 Optional 对象,否则就将该值过滤掉,将
Optional 的值置空。

异常与Optional的对比

由于某种原因,函数无法返回某个值,这时除了返回 null ,Java API比较常见的替代做法是
抛出一个异常。这种情况比较典型的例子是使用静态方法 Integer.parseInt(String) ,将
String 转换为 int 。在这个例子中,如果 String 无法解?到对应的整型,该方法就?出一个
NumberFormatException 。最后的效果是,发生 String 无法转换为 int 时,代码发出一个遭遇
非法参数的信号,唯一的不同是,这次你需要使用 try / catch 语句,而不是使用 if 条件?断来
控制一个变量的值是否非空。
你也可以用空的 Optional 对象,对遭遇无法转换的 String 时返回的非法值进行建模,这时
你期望 parseInt 的返回值是一个 optional 。我们无法修改最初的Java方法,但是这无?我们进
行需要的改进,你可以实现一个工具方法,将这部分逻辑?装于其中,最终返回一个我们希望的
Optional 对象,代码如下所示。

public static Optional<Integer> stringToInt(String s) {try {return Optional.of(Integer.parseInt(s));} catch (NumberFormatException e) {return Optional.empty();}
}

我们的建议是,你可以将多个类似的方法?装到一个工具类中,让我们称之为 Optiona-
lUtility 。通过这种方式,你以后就能直接调用 OptionalUtility.stringToInt 方法,将
String 转换为一个 Optional 对象,而不再需要记得你在其中封装了笨拙的
try / catch 的逻辑了。

总结

Optional的出现使Java对null的表达式更进一步,希望大家能够掌握API的使用方法和原理,合理使用可以避免大量的NPE,节省大量的人力物力。
Optional类支持多次方法,比如map、flatMap、filter,它们在概念上与Stream类中对应的方法十分相似。

Java Optional总结相关推荐

  1. java Optional操作

    目录 Optional对象的创建 isPresent()和isEmpty()判空处理 ifPresent()和ifPresentOrElse()的条件动作 Optional对象中获取值 orElse( ...

  2. Java Optional容器判空处理

    Java Optional容器判空处理 1. Java判空概述 2. Java判空方式 2.1 普通判空方式 2.2 Optional判空方式 1. Java判空概述 Java判空方式有很多多,可以i ...

  3. JAVA中throw用法例子,Java Optional orElseThrow()用法及代码示例

    Java中的java.util.Optional类的orElseThrow()方法用于获取此Optional实例(如果存在)的值.如果此Optional实例中不存在任何值,则此方法将引发从指定供应商生 ...

  4. java optional 用法_Java 8中的Optional: 如何正确使用?

    Java 8中出现一个新的Optional类型,和其他语言中null的替代品类似. 本文将讨论如何使用这种新类型,即它的主要用例是什么. 什么是Optional类型? Optional是对单个对象包装 ...

  5. Java Optional学习笔记

    (1) 这个Optional.of方法还是不接收null作为输入参数: 而Optional.ofNullable可以接收null参数: isPresent判断有无数据,这个没啥可以说的: 如果Opti ...

  6. java optional用法_2019年 Java 调查报告:“被取代”是不存在的!

    作者 | Nikos Vaggalis 译者 | 冬雨 策划 | 田晓旭 尽管 Java 被称为编程语言的"老马",但它仍在不断发展. 在 Tiobe 排行榜中,它仍然是的第一名, ...

  7. Java Optional的使用实践概述

    题记:不到最后时刻,千万别轻言放弃,无论结局成功与否,只要你拼博过,尽力过,一切问心无愧 Java 8是Java自Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库.工具 ...

  8. 看我发现了什么好东西? Java Optional,绝对值得一学 | 原力计划

    作者 | 沉默王二 来源 | CSDN博客 头图 | 付费下载自视觉中国 出品 | CSDN(ID:CSDNnews) 想学习,永远都不晚,尤其是针对 Java 8 里面的好东西,Optional 就 ...

  9. java optional 用法_理解、学习与使用Java中的Optional

    从Java8 引入的一个很有趣的特性是Optional类.Optional类主要解决的问题是臭名昭著的空指针异常(NullPointerException) -- 每个 Java 程序员都非常了解的异 ...

  10. java optional详解_Java 8 中的 Optional 使用详解

    1 概述Optional 的完整路径是 java.util.Optional,使用它是为了避免代码中的 if (obj != null) { } 这样范式的代码,可以采用链式编程的风格 而且通过 Op ...

最新文章

  1. 转: GridView:当鼠标滑过,行的背景颜色发生变化
  2. clock_gettime获取时间
  3. python与tensorflow的关系_Tensorflow GPU与CPU安装库的区别
  4. malloc,realloc,calloc的使用场合及特点,以及malloc的实现原理
  5. angularjs文档下载
  6. RandomStringUtils的使用
  7. 中国内地楼市泡沫严重 租售比1000倍超美国
  8. AOSP 安卓源码-ubuntu开发环境搭建
  9. Linux中inode值是什么?
  10. python中int什么意思_python中int是什么意思
  11. 分享一下自己用c++写的小地图
  12. puppet进阶指南——cron资源详解
  13. 通过css和html构建能耗数据分析树状图
  14. 【AcWing】语法基础课听课笔记
  15. DNX SDK版本 “dnx-clr-win-x86.1.0.0-beta5”无法安装
  16. Python:变身超级赛亚人
  17. 京东换新 Logo :脸蛋胖了,脖子粗了
  18. eclipse快捷键——复制当前行到上一行或者下一行
  19. 怎么将pdf转换excel转换器
  20. Mybatis中的类型转换器

热门文章

  1. 第七天;信息打点-资产泄漏CMS识别Git监控SVNDS_Store备份
  2. 1020 月饼(C语言版 + 注释 + 贪心算法)
  3. skinsharp 函数文档
  4. 英雄黎明3 恐锤java_我的世界英雄黎明3恐锤mod
  5. 平板电脑出货不忍直视,瑞芯微、全志冰火两重天
  6. wp开发(二)--获取用户篇
  7. APES75蓝牙5.2双模 PCB简要说明
  8. vue-admin登录
  9. 网页的三层结构有哪些
  10. 三分屏课件打包器远程教育版(V1.0)