点击上方“linkoffer”,

选择关注公众号高薪职位第一时间送达

01、前言

如果要给 Java 所有异常弄个榜单,我会选择将 NullPointerException 放在榜首。这个异常潜伏在代码中,就像个遥控炸弹,不知道什么时候这个按钮会被突然按下(传入 null 对象)。

还记得刚入行程序员的时候,三天两头碰到空指针异常引发的 Bug,解决完一个,又在另一处碰到。那时候师兄就教我,不要相信任何『对象』,特别是别人给你的,这些地方都加上判断。于是代码通常为会变成下面这样:

        if(obj!=null){// do something        }

有了这个防御之后,虽然不用再担心空指针异常,但是过多的判断语句使得代码变得臃肿。

假设我们存在如下对象关系

原本为了获取图中的 name 属性,原本一句代码就可以轻松完成。

Staff staff=..;staff.getDepartment().getCompany().getName();

但是很不幸,为了代码的安全性,我们不得不加入空指针判断代码。

   Staff staff=..;if (staff != null) {            Department department = staff.getDepartment();if (department != null) {                Company company = department.getCompany();if (company != null) {return company.getName();                }            }        }return "Unknown";

当其中对象为 null 时,可以返回默认值,如上所示。也可以直接抛出其他异常快速失败。

虽然上面代码变得更加安全,但是过多嵌套 if 语句降低代码整体可读性,提高复杂度。

所幸 Java 8 引入引入一个新类 Java.util.Optional,依靠 Optional 类提供 API,我们可以写出既安全又具有阅读性的代码。

还在使用 JDK 6 ?那你也别急着关闭这篇文章。可以考虑使用 Guava Optional。不过需要注意的是,Guava Optional API 与 JDK 存在差异,以下以 JDK8 Optional 为例。

02、Optional API

1)Optional#of 与 Optional#ofNullable

Optional本质是一个容器,需要我们将对象实例传入该容器中。Optional 的构造方法为 private,无法直接使用 new 构建对象,只能使用 Optional 提供的静态方法创建。

Optional 三个创建方法如下:

  • Optional.of(obj),如果对象为 null,将会抛出 NPE。
  • Optional.ofNullable(obj),如果对象为 null,将会创建不包含值的 empty Optional对象实例。
  • Optional.empty() 等同于 Optional.ofNullable(null)

只有在确定对象不会为 null 的情况使用 Optional#of,否则建议使用 Optional#ofNullable方法。

2)Optional#get 与 Optional#isPresent

对象实例存入 Optional 容器中之后,最后我们需要从中取出。Optional#get 方法用于取出内部对象实例,不过需要注意的是,如果是 empty Optional 实例,由于容器内没有任何对象实例,使用 get 方法将会抛出 NoSuchElementException 异常。

为了防止异常抛出,可以使用 Optional#isPresent 。这个方法将会判断内部是否存在对象实例,若存在则返回 true。

示例代码如下:

        Optional optCompany = Optional.ofNullable(company);// 与直接使用空指针判断没有任何区别if (optCompany.isPresent()) {            System.out.println(optCompany.get().getName());        }

仔细对比,可以发现上面用法与空指针检查并无差别。刚接触到 Optional ,看到很多文章介绍这个用法,那时候一直很疑惑,这个解决方案不是更加麻烦?

后来接触到 Optional 其他 API,我才发现这个类真正有意义是下面这些 API。如果使用过 Java8 Stream 的 API,下面 Optional API 你将会很熟悉。

3)Optional#ifPresent

通常情况下,空指针检查之后,如果对象不为空,将会进行下一步处理,比如打印该对象。

        Company company = ...;if(company!=null){            System.out.println(company);        }

上面代码我们可以使用 Optional#ifPresent 代替,如下所示:

        Optional optCompany = ...;        optCompany.ifPresent(System.out::println);

使用 ifPresent 方法,我们不用再显示的进行检查,如果 Optional 为空,上面例子将不再输出。

4)Optional#filter

有时候我们需要某些属性满足一定条件,才进行下一步动作。这里假设我们当 Company name 属性为 Apple,打印输出 ok。

        if (company != null && "Apple".equals(company.getName())) {            System.out.println("ok");        }

下面使用 Optional#filter 结合 Optional#ifPresent 重写上面的代码,如下所示:

        Optional companyOpt=...;        companyOpt                .filter(company -> "Apple".equals(company.getName()))                .ifPresent(company -> System.out.println("ok"));

filter 方法将会判断对象是否符合条件。如果不符合条件,将会返回一个空的 Optional 。

5)Optional#orElse 与 Optional#orElseThrow

当一个对象为 null 时,业务上通常可以设置一个默认值,从而使流程继续下去。

  String name = company != null ? company.getName() : "Unknown";

或者抛出一个内部异常,记录失败原因,快速失败。

        if (company.getName() == null) {throw new RuntimeException();        }

Optional 类提供两个方法 orElse 与 orElseThrow ,可以方便完成上面转化。

   // 设置默认值  String name=companyOpt.orElse(new Company("Unknown")).getName();

// 抛出异常    String name=companyOpt.orElseThrow(RuntimeException::new).getName();

如果 Optional 为空,提供默认值或抛出异常。

6)Optional#map 与 Optional#flatMap

熟悉 Java8 Stream 同学的应该了解,Stream#map 方法可以将当前对象转化为另外一个对象, Optional#map 方法也与之类似。

        Optional optCompany = ...;        Optional nameopt = optCompany.map(Company::getName);

map 方法可以将原先 Optional 转变成 Optional ,此时 Optional 内部对象变成 String 类型。如果转化之前 Optional 对象为空,则什么也不会发生。

另外 Optional 还有一个 flatMap 方法,两者区别见下图。

Department#getCompany返回对象为 Optional

03、代码重构

上面我们学习了 Optional 类主要 API ,下面我们使用 Optional 重构文章刚开头的代码。为了方便读者对比,将上面的代码复制了下来。

代码重构前:

        if (staff != null) {            Department department = staff.getDepartment();if (department != null) {                Company company = department.getCompany();if (company != null) {return company.getName();                }            }        }return "Unknown";

首先我们需要将 StaffDepartment 修改 getter 方法返回结果类型改成 Optional

 public class Staff {private Department department;public OptionalgetDepartment() {return Optional.ofNullable(department);       }     ...   }public class Department {

private Company company;public OptionalgetCompany() {return Optional.ofNullable(company);     }     ...   }

public class Company {private String name;public String getName() {return name;        }     ...   }

然后综合使用 Optional API 重构代码如下:

        Optional staffOpt =...;        staffOpt                .flatMap(Staff::getDepartment)                .flatMap(Department::getCompany)                .map(Company::getName)                .orElse("Unknown");

可以看到重构之后代码利用 Optional 的 Fluent Interface,以及 lambda 表达式,使代码更加流畅连贯,并且提高代码整体可读性。

参考资料

[1]

Tired of Null Pointer Exceptions? Consider Using Java SE 8's Optional!: https://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

[2]

Optionals: Patterns and Good Practices: https://community.oracle.com/docs/DOC-991686

optional用法_还在重复写空指针检查代码?考虑使用 Optional 吧!相关推荐

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

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

  2. python文件批量重命名_文件批量重命名的python代码

    我们下载大量文件,或者拍摄大量照片.视频之后,或许会有一些人想要给它们重命名,以使文件们看起来更整洁一些. 用python是可以轻松解决的,而且只需要用几行代码.这对于程序员是一件很简单不过的事了,但 ...

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

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

  4. 现在的便签本都这么社会了!?重复写万次还能云端保存

    不知模友们有没有感觉到 每天脑子要记的事情越来越多 不说事情杂七杂八 重点是精力有限 刚说完的事转眼就忘了 想过在电脑贴满便利贴 想过本子记录好每天要做什么 But ...... 每天忙的天昏地黑 啥 ...

  5. java十五章_Java8 教程第十五章之Optional 用法

    本套JAVA8教程由于是有英文翻译过来的,如果有翻译不对的地方还请多多包涵. 本节课先简单的介绍下Java8有哪些新特性,对于Java6/7版本做出哪些更改.那废话不多说,赶紧开始今天的课程吧. 引子 ...

  6. python global用法_【python测试开发栈】python基础语法大盘点

    周边很多同学在用python,但是偶尔会发现有人对python的基础语法还不是特别了解,所以帮大家梳理了python的基础语法(文中的介绍以python3为例).如果你已然是python大牛,可以跳过 ...

  7. 对象空指针_可选和对象:空指针救星!

    对象空指针 没有人喜欢空指针异常 ! 我们有办法摆脱它们吗? 也许 . . . 这篇文章中讨论了几种技术: 可选类型(Java 8中的新增功能) 对象类(旧的Java 7东西!) Java 8中的可选 ...

  8. java中避免空指针_在Java中避免空检查

    java中避免空指针 对于Java开发人员(从初级到专家)最糟糕的噩梦之一是空对象引用检查. 我很确定您已经看过几次这样的代码: public void addAddressToCustomer(Cu ...

  9. java8 optional使用_[Java8]如何正确使用Optional

    Optional是Java8提供的为了解决null安全问题的一个API.善用Optional可以使我们代码中很多繁琐.丑陋的设计变得十分优雅.这篇文章是建立在你对Optional的用法有一定了解的基础 ...

最新文章

  1. 长方形纸做容积最大的长方体_A4纸的尺寸是怎么来的?
  2. java 协程线程的区别_为什么 Java 坚持多线程不选择协程?
  3. c语言case的值能动态修改吗,java中的switch case语句中,case所对应的数目是不确定的,能否动态改变case...
  4. ROS中阶笔记(十):ROS机器人综合应用
  5. 解决PopupWindow的阴影覆盖问题
  6. windows 批处理脚本(batch scripting)
  7. [译]简单声明Lua类
  8. Unity采用Forge Networking Remastered数据的远程传输Basic Moving Cube Example
  9. 几个不错的网站(转)
  10. 书单 | 做数字化转型,离不开这10本书!
  11. Robomongo的安装和配置
  12. delphi 企业微信消息机器人_企业微信群消息机器人发送开源项目
  13. ESP-IDF:ESP32 fatory分区不够用,重新分区的方法
  14. 更改tkinter的OptionMenu背景颜色和下拉菜单宽度
  15. 【C语言】极坐标转换为直角坐标
  16. Vue Echarts 电子支付图表
  17. lua 区间比较_自然区间匹配算法 - borey的个人空间 - OSCHINA - 中文开源技术交流社区...
  18. Android Pie(9.0) 行为变更
  19. Linux文件系统笔记
  20. 斯坦福SCI论文写作课笔记(一)

热门文章

  1. UI干货素材分享|导航设计
  2. git查看两次提交之间的差异_如何在同一分支的两个不同提交之间区分同一文件?...
  3. arraylist扩容是创建新数组吗 java_Java 集合,你肯定也会被问到这些
  4. 面向过程(或者叫结构化)分析方法与面向对象分析方法到底区别
  5. LLVM PHI - if else
  6. 【HTML+CSS网页设计与布局 从入门到精通】第14章-float/position/display属性
  7. unicode编码表_6-字符编码-文件处理
  8. python基础(笔记)
  9. java里面的三角函数
  10. 电脑桌面游戏_二次元游戏的高人气角色,不管男女都有一个特点,是巧合吗?...