概述

在Java8之前, 如果需要对一个变量做一次 null 检查, 通常会像下面这样写

    T t = service1.query();
if (t != null) {
K k = service2.update(t);
if (k != null) {
U u = service3.save(k);
}
}

如果业务比较复杂, 可能会像上面那样, 使用 if 语句进行多层嵌套, 后期难以扩展。

在Java8中新引入了一个 Optional 类, Optional 类会对可能为 null 值的变量进行建模, 这样你就不必直接将 null 赋值给变量, 也就不必像上面那样进行多层嵌套检查。

在正式介绍 Optional 类的 API之前, 先引入一些实体类, 后面的代码示例会经常用到它们。

@Data
public class Person { private Car car;
private int age; public Optional<Car> getCar() {
return Optional.ofNullable(car);
}
} @Data
public class Car { private Insurance insurance; public Optional<Insurance> getInsurance() {
return Optional.ofNullable(insurance);
}
} @Data
public class Insurance { private String name;
}

创建Optional对象

Optional 类提供了三种方式来创建 Optional 对象

  • empty(): 创建一个空的 Optional 对象
  • of(): 将指定值用 Optional 封装之后返回,如果该值为 null ,则抛出一个 NullPointerException 异常
  • ofNullable(): 将指定值用 Optional 封装之后返回,如果该值为 null ,则返回一个空的 Optional 对象
    @Test
public void test1() {
//声明一个空的optional
Optional<Car> c1 = Optional.empty();
//根据一个非空的值创建optional, 如果传入的值是null,会抛出空指针异常
Optional<Car> c2 = Optional.of(new Car());
//可接受null的Optional
Optional<Object> c3 = Optional.ofNullable(null);
}

flatMap和map用法

flatMap()和map()都是在指定值存在时, 就对该值执行提供的 mapping 函数调用, 返回一个 Optional 类型的值, 否则就返回一个空的 Optional 对象。

注意 flatMap() 方法的转换函数是 Function<? super T, Optional<U>> mapper , 这个特性和前面介绍的 Stream 类的 flatMap() 很像。

查看一下 Optional 类的源码, flatMap() 会将 Optional<T> 中的 T 转换成 Optional<U>, 再将转换后的 Optional<U> 直接作为方法返回值返回。

    @Test
public void test2() {
Optional<Person> person = Optional.ofNullable(new Person());
String name = person.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("哪吒之魔童降世");
System.out.println(name); //result: 哪吒之魔童降世
}

上面的代码示例中,  因为 Person 实体类中 car 属性是一个 null 值, 所以  person.flatMap(Person::getCar) 这个操作会得到一个空的Optional 对象。

这样就导致了后面的 flatMap() 和 map() 两次转换都返回一个空的 Optional 对象。orElse() 方法的含义是如果有值则将其返回,否则返回一个默认值。所以最后的输出结果是默认值  哪吒之魔童降世 。

在实际业务中, 你可以像下面这样将 Optional 作为方法入参和返回值使用

    @Test
public void test3() {
Optional<Insurance> insurance1 = find(Optional.of(new Person()), Optional.empty());
System.out.println(insurance1);//result: Optional.empty
Optional<Insurance> insurance2 = find(Optional.of(new Person()), Optional.of(new Car()));
System.out.println(insurance2);//result: Optional[Insurance(name=null)]
} public Optional<Insurance> find(Optional<Person> person, Optional<Car> car) {
return person.flatMap(p -> car.map(c -> find(p, c)));
} private Insurance find(Person p, Car c) {
return new Insurance();
}

filter和ifPresent

  • filter(): 如果值存在并且满足提供的谓词, 就返回包含该值的 Optional 对象; 否则返回一个空的 Optional 对象
  • ifPresent(): 如果值存在, 就执行使用该值的方法调用, 否则什么也不做
    @Test
public void test4() {
Optional.of("攀登者").filter(i -> i.length() < 4).ifPresent(System.out::println);//打印: 攀登者
Optional.<String>empty().filter(i -> i.length() < 4).ifPresent(System.out::println);//没有输出
}

下面是一个测试示例, 到本地去运行看下输出结果, 它可以帮你重新理解一下前面介绍的几个 API 的用法。

    @Test
public void test5() {
System.out.println(getCarInsuranceName(Optional.empty(), 8));//result: 命运之夜
Person p = new Person();
p.setAge(10);
System.out.println(getCarInsuranceName(Optional.of(p), 8));//result: 命运之夜
Insurance insurance = new Insurance();
insurance.setName("知否知否");
Car car = new Car();
car.setInsurance(insurance);
p.setCar(car);
System.out.println(getCarInsuranceName(Optional.of(p), 8));//result: 知否知否
} //找出年龄大于或者等于 minAge 参数的 Person 所对应的保险公司列表。
public String getCarInsuranceName(Optional<Person> person, int minAge) {
return person.filter(p -> p.getAge() >= minAge).flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("命运之夜");
}

Optional与异常的对比

通常在由于某种因素下, 函数无法正确返回某个值, 常见的做法就是使用 try/catch 语句处理返回一个null值, 或者不做任何处理直接抛出一个异常。

如果函数无法正确返回某个值, 且你不需要它抛出异常, 而是要它返回一个默认值, 那么 Optional 可以帮您更优雅的实现

    @Test
public void test6() {
String value = "test";
Integer res = Optional.of(value).flatMap(this::str2Int).filter(i -> i > 0).orElse(0);
System.out.println(res);//result: 0
} private Optional<Integer> str2Int(String str) {
try {
//如果能正确解析, 就将其封装在 Optional 中返回
return Optional.of(Integer.valueOf(str));
} catch (NumberFormatException ex) {
//如果解析发生异常, 就返回一个空的 Optional
return Optional.empty();
}
}

上面的代码清单,   Integer.valueOf() 会在入参不是一个整型数值时, 抛出 NumberFormatException , 我们这里对它做了处理, 当解析发生异常时, 就返回一个空的 Optional 对象。

上面的用例只有在 value 变量是大于零的整型数值时, 才会输出 value 变量的值, 否则, 输出结果都是默认值 0。

总结

最后, 来一张大图

参考资料

Java8 实战

作者:张小凡
出处:https://www.cnblogs.com/qingshanli/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】。

Java8系列 (五) Optional类的更多相关文章

  1. Java8 新特性 Optional 类

    Optional 类的简介   Optional类的是来自谷歌Guava的启发,然后就加入到Java8新特性中去了.Optional类主要就是为子决解价值亿万的错误,空指针异常.   Optional ...

  2. Java基础复习笔记系列 五 常用类

    Java基础复习笔记系列之 常用类 1.String类介绍. 首先看类所属的包:java.lang.String类. 再看它的构造方法: 2. String s1 = “hello”: String ...

  3. Java8新特性——Optional类的使用(有效的避免空指针异常)

    OPtional类的使用 概述 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因.以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guav ...

  4. 使用Java8中的Optional类来消除代码中的null检查

    简介 Optional类是Java 8新增的一个类,Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException). —— 每个 Java 程序员都非常了解的异常 ...

  5. Java多线程系列五——列表类

    参考资料: http://xxgblog.com/2016/04/02/traverse-list-thread-safe/ 一些列表类及其特性  类 线程安全 Iterator 特性 说明 Vect ...

  6. Java8新特性——Optional

    前言 在开发中,我们常常需要对一个引用进行判空以防止空指针异常的出现.Java8引入了Optional类,为的就是优雅地处理判空等问题.现在也有很多类库在使用Optional封装返回值,比如Sprin ...

  7. Java8新特性之空指针异常的克星Optional类

    Java8新特性系列我们已经介绍了Stream.Lambda表达式.DateTime日期时间处理,最后以"NullPointerException" 的克星Optional类的讲解 ...

  8. Java8 Lambda表达式、Optional类浅析

    1.概念 Lambda是一个匿名函数,可以将其理解为一段可以传递的代码(将代码像数据一样进行传递)可以写出更简洁.更灵活的代码.作为一种更紧凑的代码风格,使得java语言的表达能利得到了提升. 2. ...

  9. Java8之Optional类

    写在前头 今天再看阿里的Java开发手册,里面异常处理第10条提到这样一个建议. [推荐]防止 NPE ,是程序员的基本修养,注意 NPE 产生的场景:1 ) 返回类型为基本数据类型,return 包 ...

随机推荐

  1. Spring boot 梳理 - @SpringBootConfiguration

    @SpringBootConfiguration继承自@Configuration,二者功能也一致,标注当前类是配置类, 并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到sprin ...

  2. Gradle 梳理:安装、入门使用方法

    Gradle 教程:第一部分,安装[翻译]   原文地址:http://rominirani.com/2014/07/28/gradle-tutorial-part-1-installation-se ...

  3. asp.net core 使用Mysql和Dapper

    序曲:学习编程最好的方式就是敲代码,没有比这个更好的方法,哪怕你看了上百G的视频,都不如你自己敲几行代码更为有效.还有要记得敲完代码然后写一篇随笔来记录一下你所学所想. 大家都知道,.netcore是 ...

  4. Java中的static(1)【持续更新】——关于Eclipse的No enclosing instance of type ... 错误的理解和改正

    No enclosing instance of type SomeClass is accessible. Must qualify the allocation with an enclosing ...

  5. python Django中的cookie和session

    目录 Cookie 1.1获取Cookie 1.2设置Cookie Session 1.数据库Session 2.缓存Session 3.文件Session 4.缓存+数据库Session Cooki ...

  6. Nepxion Discovery【探索】微服务企业级解决方案

    Nepxion Discovery[探索]微服务企业级解决方案] Nepxion Discovery[探索]使用指南,基于Spring Cloud Greenwich版.Finchley版和Hoxto ...

  7. JavaSE----02.Java语言基础

    02.Java语言基础 1.关键字     Java关键字是电脑语言里事先定义的,有特别意义的标识符,有时又叫保留字,还有特别意义的变量.Java的关键字对Java的编译器有特殊的意义,他们用来表示一 ...

  8. 设计模式----行为型模式之观察者模式(Observer Pattern)

    下面是阅读<Head First设计模式>的笔记. 观察者模式 定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新. JDK API内置机制 ...

  9. 关于MySQL的经典例题50道

    --1.学生表Student(S,Sname,Sage,Ssex) --S 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别--2.课程表 Course(C,Cname,T) - ...

  10. MIPI CSI2-TX接口基于FPGA实现

    MIPI CSI2-TX用途: 跟海思的3559A芯片进行图像数据传输: MIPI CSI2-TX接口特性: xilinx 7系列芯片最大支持1.25Gbps: 最大支持lanes数量为4: 支持的图 ...