为什么使用Java Optional

Why use Optional? NullPointerException

有个很有名的说法: Null Pointer References: The Billion Dollar Mistake,这是发明null指针的人提出来的。(https://medium.com/@hinchman_amanda/null-pointer-references-the-billion-dollar-mistake-1e616534d485)

为什么说空指针是十亿美元的错误,其实不光是java,在其他编程语言中,也经常会遇到空指针异常的问题。可以说这是一个编程中出现最频繁的bug,有人说70%以上的异常都是NullPointerException。我们可以看下代码:

String str=new String("hello world");
str.trim();

如果我们只是写个简单的Demo,new一个简单对象,然后调用,当然ok, 不会有任何问题。但是一旦到了真实项目中,就会有很多问题,因为实际编程环境往往复杂得多,代码如下:

String str=getMsgFormDB();
str=str.trim(); // 或者
str=str.replace(getMsgFromWebService());
str=str.trim();
  • 问题在哪里,哪一步会抛异常,抛出什么异常(不考虑调用方法抛出异常的情况)?

通常会在str.trim()或str.append()这一步出现NullPointerException,因为str可能会是一个null值,而调用null对象的方法就是空指针异常了。

那么我们一般是怎么那避免这个问题?如果有过一些实战经验的,不管用什么语言开发我们多少都会在踩坑培养出谨慎态度,哪就是不相信任何被调用的代码(包括自己写的方法),比如上面代码我们会这样写:

String str=getMsgFormDB();

if(null != str){
str.trim();
str=str.replace(getMsgFromWebService());
} if(null != str){
str= str.trim();
}

这样写,就保证了程序的健壮性,杜绝了NullPointerException。但是这就带来了一个问题,代码变得非常啰嗦,不可读性,想想看如果我们写一个比较复杂的业务,我们会写一堆非空检查,而且会层层嵌套,如:

if (user != null) {
Address address = user.getAddress();
if (address != null) {
Country country = address.getCountry();
if (country != null) {
String isocode = country.getIsocode();
if (isocode != null) {
isocode = isocode.toUpperCase();
}
}
}
}

那么有没有办法即保证程序健壮性的同时,又保持代码的优雅和可读性,Optional就是在java 8中提出的解决方案,看如下代码:

String str= Optional.ofNullable(getMsgFormDB())                    //1
.map(d->d.trim()) //2
.map(d->d.replace(getMsgFromWebService())) //3
.map(d->d.trim()) //4
.orElseGet(() -> Strings.EMPTY); //5

上面的这一行代码实际上是一个连续的调用链,使用了lambda表达式和Optional多个函数,我们可以把它如下拆开来分析,代码如下:

String str;

//1. 这是用Optional包装了String的可空对象
Optional<String> strOpt= Optional.ofNullable(getMsgFormDB()); //2. map函数是将Optional包装的对象转换成其他对象,可以是其他类型对象,这里只是将String转换成不同值String对象,并且map函数是有一个隐含逻辑的,就是只有Optional里面第二次String 不为null时才执行你传入map中的Lambda表达式。
strOpt= strOpt.map(d->d.trim()); //3 ...
strOpt= strOpt.map(d->d.replace(getMsgFromWebService()))
//4 ...
strOpt= strOpt.map(d->d.trim())
//5 orElseGet函数的意思是,如果strOpt中String对象为null,则执行传入其中的表达式,这一行是返回了一个空字符串。Optional还有一个类似的orElse(),它和orElseGet区别是,它返回一个Optional包装类型对象
strOpt= strOpt.orElseGet(() -> Strings.EMPTY);

上面的代码和上上面的代码,其整个组成的逻辑就是,用Optional包装从调用getMsgFormDB()方法返回的对象,如果getMsgFormDB()返回的对象不为null,将其转换成一个去除空格的String对象,并一次执行第三不,第四步的转换。 如果getMsgFormDB()返回的对象为null,则不执行2,3,4这几个传入map函数中的表达式,我们且当做未执行2,3,4这三行代码,直接执行了第5行代码。

以上示例展示了如何使用Optional类来组装我们的代码,它可以使我们的代码变成一个链式的,易于阅读的,且一定曾读增强了代码的健壮性。Optional类还有许多其他函数来做一些我们需要的常规操作,如: isPresent(),isPresent(),isPresent(),ifPresent等等,可参考如下教程: https://www.runoob.com/java/java8-optional-class.html

另外有一个实践建议,我不是很清楚它是否完全正确,我们可以权衡一下。

  • 我们应该尽量避免在代码中范型型参,方法入参或返回类型类型中使用Optional,如:
// 可以这样用

public void youMethod(){
Any str=Optional.ofNullable(getObjectByOthers()).orElseGet(()-> new Any()); //... doSomething
} // 不要这样做
public Optional<Any> youMehtod(Optional<Any> arg){ }

为什么要使用Optional的更多相关文章

  1. [翻译]理解Swift中的Optional

    原文出处:Understanding Optionals in Swift 苹果新的Swift编程语言带来了一些新的技巧,能使软件开发比以往更方便.更安全.然而,一个很有力的特性Optional,在你 ...

  2. Atitit. null错误的设计 使用Optional来处理null

    Atitit. null错误的设计 使用Optional来处理null 然后,我们再看看null还会引入什么问题. 看看下面这个代码: String address = person.getCount ...

  3. Guava学习笔记(1):Optional优雅的使用null

    转自:http://www.cnblogs.com/peida/archive/2013/06/14/Guava_Optional.html 参考:[Google Guava] 1.1-使用和避免nu ...

  4. guava – Optional

    过多的使用null可能会导致大量的bugs,Google code 底层代码中,95%的集合类默认不接受null值.对null值,使用快速失败拒绝null比默认接受更好. 另外,null本身的含义很模 ...

  5. JAVA 8 Optional类介绍及其源码

    什么是Optional对象 Java 8中所谓的Optional对象,即一个容器对象,该对象可以包含一个null或非null值.如果该值不为null,则调用isPresent()方法将返回true,且 ...

  6. maven可选依赖(Optional Dependencies)和依赖排除(Dependency Exclusions)

    我们知道,maven的依赖关系是有传递性的.如:A-->B,B-->C.但有时候,项目A可能不是必需依赖C,因此需要在项目A中排除对A的依赖.在maven的依赖管理中,有两种方式可以对依赖 ...

  7. Java 8 Optional类深度解析

    身为一名Java程序员,大家可能都有这样的经历:调用一个方法得到了返回值却不能直接将返回值作为参数去调用别的方法.我们首先要判断这个返回值是否为null,只有在非空的前提下才能将其作为其他方法的参数. ...

  8. Swift中的Optional类型 (可选类型)与强制解包 ? !

    我们在swift的开发中会经常遇见?和! ,理解这两个符号深层次的内容对我们的开发是相当有利的: 目前网上对swift3.0的教程还相当的少,如果去搜索会发现早期的说法,在定义变量的时候,swift是 ...

  9. Swift开发第五篇——四个知识点(Struct Mutable方法&Tuple&autoclosure&Optional Chain)

    本篇分三部分: 一.Struct Mutable方法 二.多元组(Tuple) 的使用 三.autoclosure 的使用 四.Optional Chain 的使用 一.Struct Mutable方 ...

  10. initializer for conditional binding must have optional type not AVAudioPlayer

    if let buttonBeep = self.setupAudioPlayerWithFile("ButtonTap", type: "wav") {    ...

随机推荐

  1. REDIS中加锁和解锁问题

    使用lua+redis的方法.之所以使用lua是为了保证原子性 问题: 1. redis发现锁失败了要怎么办?中断请求还是循环请求?2. 循环请求的话,如果有一个获取了锁,其它的在去获取锁的时候,是不 ...

  2. vue子组件数据变化同步到父组件中

    方法:通过watch监听子组件数据变化 1.父组件中注册方法 <Child @getChildValue="getChildValue"></Child> ...

  3. mongodb的开机自启动

    一.背景 Linux轻松的在rc.local中写上启动脚本,reboot~发现没有启动成功.这不科学啊,查看日志发现“permission denied” 二.解决 Linux系统下,使用自定配置文件 ...

  4. jenkins持续集成springboot

    1.可以配置项目标签 PS:这里Default Value值修改为master 2.代码地址,这里并没有拉取制定标签的代码,而是拉取了最新版本代码,如果要拉取指定标签代码需要把*/master修改成$ ...

  5. linux查看 LAMP环境安装路径

    Apache: 如果采用RPM包安装,安装路径应在 /etc/httpd目录下 apache配置文件:/etc/httpd/conf/httpd.conf Apache模块路径:/usr/sbin/a ...

  6. ZingChart line 折线图表数据设置

    根据 x 坐标和 y 坐标一一对应进行设置 (x,y) { "type": "line", "series":[ {,],[,],[,],[ ...

  7. 002——Angular 目录结构分析、app.module.ts 详解、以及 Angular 中创建组件、组件 详解、 绑定数据

    一.目录结构分析 二. app.module.ts.组件分析 1.app.module.ts 定义 AppModule,这个根模块会告诉 Angular 如何组装该应用. 目前,它只声明了 AppCo ...

  8. SAP RFC和BAPI

    RFC和BAPI都是SAP提供的接口技术.RFC全称Remote Function Call,就是允许远程调用的函数模块. BAPI则是基于RFC的新技术,全称Business Application ...

  9. 使用idea创建简单的webservice服务

     New project: 生成HelloWorld.wsdl: 配置好tomcat后还需要加入 Axis 的库: 启动后,访问http://localhost:8080/services: 点击He ...

  10. IBM System x3650 M3_RAID服务器进入阵列卡配置界面(webBIOS)

    按 F1 进入 UEFI, 选择“System Settings”- “Adapters and UEFI Drivers”- “Please press ENTER to compile the l ...