原创文章,转载请标注出处:https://www.cnblogs.com/V1haoge/p/10755368.html

一、概述

Optional的引入是为了解决null的问题,那么到底是解决null的什么问题呢?

我们知道当我们针对null调用方法的之后,就会抛出空指针异常,Optional就是为了解决这个问题而来的。

Optional通过封装目标对象的方式来表示,当我们使用的时候,Optional是必然存在的,因为如果结果为null,会返回一个固定的EMPTY实例,这样就不会存在null引用的问题了。

那该如何来使用Optional呢?

我们不能滥用Optional,只有在逻辑上(业务上)可能为null时才使用Optional来封装目标对象,如果逻辑上(业务上)肯定不可能为null时,就不要使用Optional封装对象,这样当阅读代码的人看到有Optional则表示目标对象是可以为null的。

这样之后,如果程序中又出现了null指针异常,那么只能是你的代码逻辑有误,而不可能是非逻辑原因了,也就是不该为null的时候出现了null,这肯定是你逻辑搞错了。

二、Optional

2.1 创建Optional实例

2.1.1 创建一个空的Optional

  1. public class OptionalTest {
  2. public static void main(String[] args) {
  3. Optional<String> o = Optional.empty();// 创建一个空Optional
  4. }
  5. }

2.1.2 创建一个有值的Optional

  1. public class OptionalTest {
  2. public static void main(String[] args) {
  3. Optional<String> op = Optional.of("123");// 创建一个目标对象必须有值的Optional
  4. }
  5. }

这种创建方式,如果of的参数为null,则直接抛出异常。

2.1.3 创建一个可为空的Optional

  1. public class OptionalTest {
  2. public static void main(String[] args) {
  3. Optional<String> opt = Optional.ofNullable(null);// 创建一个目标对象可为null的Optional
  4. }
  5. }

2.2 中间操作-处理Optional

2.2.1 filter

这个filter是一个校验器,由于Optional中也只有一个元素,称其为过滤有点太大了,如果Optional中的元素满足给定的校验条件,则将封装元素的Optional返回,否则返回空的Optional。

  1. public class OptionalTest {
  2. public static void main(String[] args) {
  3. filterTest();
  4. }
  5. public static void filterTest(){
  6. Optional<String> os = Optional.of("123456").filter(e -> e.length()>7);
  7. System.out.println(os.isPresent());
  8. }
  9. }

执行结果:

  1. false

因为字符串“123456”长度小于7,所以返回一个空的Optional。

2.2.2 map

map是映射之意,就是针对Optional封装的元素进行操作,然后返回一个封装着操作结果的新的Optional。

  1. public class OptionalTest {
  2. public static void main(String[] args) {
  3. mapTest();
  4. }
  5. public static void mapTest(){
  6. Optional<Integer> oi = Optional.of("abcdefg").map(e -> e.length());
  7. System.out.println(oi.get());
  8. }
  9. }

执行结果:

  1. 7

2.2.3 flatMap

这个方法是扁平化映射,所谓扁平就是处理嵌套的Optional。

比如有一个Person类,其中有一个Optional类型的字段name,我们在别处获取到了一个Optional类型的实例,现在想要获取到这个Person的的姓名,这就是嵌套的Optional。

  1. class Person{
  2. Optional<String> name;
  3. public Person(String name){
  4. this.name = Optional.of(name);
  5. }
  6. public Optional<String> getName(){
  7. return name;
  8. }
  9. }

如果我们使用map获取到的是如下的:

  1. public class OptionalTest {
  2. public static void main(String[] args) {
  3. flatMapTest();
  4. }
  5. public static void flatMapTest(){
  6. Optional<Person> op = Optional.of(new Person("huahua"));
  7. Optional<Optional<String>> oos = op.map(Person::getName);
  8. String name = oos.get().orElseGet(()->"noName");
  9. System.out.println(name);
  10. }
  11. }

而我们使用flatMap的话就可以是这样的:

  1. public class OptionalTest {
  2. public static void main(String[] args) {
  3. flatMapTest();
  4. }
  5. public static void flatMapTest(){
  6. Optional<Person> op = Optional.of(new Person("huahua"));
  7. Optional<String> os = op.flatMap(Person::getName);
  8. String name = os.orElseGet(()->"noName");
  9. System.out.println(name);
  10. }
  11. }

嵌套的Optional要获取到目标对象,必须通过多次去壳才行,这也是使用map映射的原理,但是使用flatMap,扁平化功能,一次去壳操作就完成了。

2.3 从Optional中获取目标值

有四种方式:

  • public T get():直接获取Optional中的值,如果是空的要抛出NoSuchElementException异常
  • public T orElse(T other):如果Optional不为空,则将值返回,否则返回指定的other(默认值)
  • public T orElseGet(Supplier<? extends T> other):如果Optional不为空,则返回值,否则将通过指定方式other生成的值返回
  • public T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X:如果Optional不为空,返回值,否则抛出由指定的exceptionSupplier生成的异常
  1. public class OptionalTest {
  2. public static void main(String[] args) {
  3. getTest();
  4. orElseTest();
  5. }
  6. public static void getTest(){
  7. Optional<String> os = Optional.of("123456");
  8. System.out.println(os.get());
  9. }
  10. public static void orElseTest(){
  11. Optional<String> os = Optional.empty();
  12. System.out.println(os.orElse("default"));
  13. System.out.println(os.orElseGet(()->"default"));
  14. System.out.println(os.orElseThrow(RuntimeException::new));
  15. }
  16. }

执行结果:

  1. 123456
  2. default
  3. default
  4. Exception in thread "main" java.lang.RuntimeException
  5. at java.util.Optional.orElseThrow(Optional.java:290)
  6. at com.dh.stream.OptionalTest.orElseTest(OptionalTest.java:29)
  7. at com.dh.stream.OptionalTest.main(OptionalTest.java:17)

三、使用方式

Optional使用时不能直接get,这时会走进老路,get的时候可能为null,就会抛出异常,这时候你就会想要在get之前进行isPresent判断,这个不使用Optional又有什么区别的。

我们使用Optional就是为了简化null判断,所以拒绝使用get方法,Optional提供了正确使用的方法是:orElse、orElseGet、orElseThrow三个方法。

使用orElse方法,我们可以从非空的Optional中获取到值,如果是空的Optional,可以返回orElse方法参数指定的默认值。

使用orElseGet方法,我们可以在空的Optional的情况下主动构建一个默认返回结果。

orElseThrow方法,在空Optional的情况下会抛出一个指定的异常。

四、总结

说了这么多,最终是为了在开发中使用Optional,正如开始时说的,我们要明确Optional规避的是那种null,不能在所有的地方都使用它。

当项目的业务规则下某个对象可能为null(就是业务允许的null),这种情况下,出现null是正常现象,这种情况需要规避,我们使用Optional封装目标对象,保证不会存在null调用抛出空指针。

但是如果在业务规则下某个对象不可能为null(就是业务不允许为null),这种情况下,出现null就是程序出错了,并不是正常现象,这种时候我们不能用Optional去封装目标对象来规避问题,而是要直接使用,一旦出错就可以及时的排查问题,不至于被Optional将问题给隐藏掉。

Java基础系列-Optional的更多相关文章

  1. Java基础系列-Collector和Collectors

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/10748925.html 一.概述 Collector是专门用来作为Stream的coll ...

  2. 2015年12月28日 Java基础系列(六)流

    2015年12月28日 Java基础系列(六)流2015年12月28日 Java基础系列(六)流2015年12月28日 Java基础系列(六)流

  3. Java基础系列--static关键字

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/8477914.html 一.概述 static关键字是Java诸多关键字中较常使用的一个,从 ...

  4. Java基础系列-ArrayList

    原创文章,转载请标注出处:<Java基础系列-ArrayList> 一.概述 ArrayList底层使用的是数组.是List的可变数组实现,这里的可变是针对List而言,而不是底层数组. ...

  5. Java基础系列-二进制操作

    原创文章,转载请标注出处:<Java基础系列-二进制操作> 概述 Java源码中涉及到大量的二进制操作,非常的复杂,但非常的快速. Java二进制表示法 首先了解下二进制,二进制是相对十进 ...

  6. Java基础系列-equals方法和hashCode方法

    原创文章,转载请标注出处:<Java基础系列-equals方法和hashCode方法> 概述         equals方法和hashCode方法都是有Object类定义的. publi ...

  7. Java基础系列-Comparable和Comparator

    原创文章,转载请标注出处:<Java基础系列-Comparable和Comparator> 一.概述         Java中的排序是由Comparable和Comparator这两个接 ...

  8. Java基础系列--HashMap(JDK1.8)

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/10022092.html Java基础系列-HashMap 1.8 概述 HashMap是 ...

  9. 夯实Java基础系列1:Java面向对象三大特性(基础篇)

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 [https://github.com/h2pl/Java-Tutorial](https: ...

随机推荐

  1. python自动化测试中的数据驱动unittest+ddt

    ddt是一个unittest的插件,用来实现uniitest的数据驱动 本文以python自动化测试中的数据驱动为原则,记录学习ddt的过程 一.数据的传递规则

  2. Java数据结构——循环链表

    一.单向循环链表表中的最后一个节点的指针域指向头结点,整个链表形成一个环.其他的与单链表相同. (以下图片均来自网络,侵删) 插入操作 删除操作 简单实现 public class CiNode { ...

  3. new操作符创建对象的四个步骤

    new操作符创建对象可以分为以下四个步骤: 创建一个空对象 将所创建对象的__proto__属性值设为构造函数的prototype的属性值 执行构造函数中的代码,构造函数中的this指向该对象 返回对 ...

  4. Vue 通过调用百度API获取地理位置-经度纬度省份城市

    一.首先在百度api注册获得ak密钥 二.新建js文件,我命名为loadBMap.js,里面创建script,代码如下: /** * 加载地图 * @param {Function} callback ...

  5. Mac包管理神器:Home-brew

    最近看到一个大神修改的Homebrew国内脚本,安装非常方便,以前使用国外的经常下载不下来,这个感觉是非常快的. Homebrew 国内自动安装脚本 ,修改原脚本中的 clone 操作为“浅拷贝”(- ...

  6. .NET Core3.1 Dotnetty实战第三章

    一.概要 本章主要内容就是讲解如何在dotnetty的框架中进行网络通讯以及编解码对象.数据包分包拆包的相关知识点. 后续会专门开一篇避坑的文章,主要会描述在使用dotnetty的框架时会遇到的哪些问 ...

  7. 理解Django 中Call Stack 机制的小Demo

    1.工作流程 request/response模式下,request并不是直接到达view方法,view方法也不是将返回的response直接发送给浏览器的,而是request由外到里的层层通过各种m ...

  8. CPU有个禁区,内核权限也无法进入!

    神秘项目 我是CPU一号车间的阿Q,是的,我又来了. 最近一段时间,我几次下班约隔壁二号车间虎子,他都推脱没有时间,不过也没看见他在忙个啥. 前几天,我又去找他,还是没看到他人,却意外地在他桌上发现了 ...

  9. 在 Visual Studio for Mac 中编译和生成

    使用Visual Studio将C#生成DLL文件的方法 https://www.cnblogs.com/AaronBlogs/p/6840283.html Visual Studio 开发 - Vi ...

  10. java返回树形结构的正确姿势

    业务场景 通常我们前端需要一个树形的导航菜单或者分类菜单,如后台权限管理中的权限树,亦或者下面例子中商城系统的商品分类多级菜单(一般为三级菜单) 数据库设计 数据库设计,采用parentId来指向自己 ...