• 《Java8实战》读书笔记09:用 Optional 处理值为 null 的情况


    大名顶顶的NullPointerException从我们入坑起就阴魂不散。我也增想过每次都要 if(obj != null) 代码太难看,但是很多时候不判又不严谨,是不是要封装一个工具类来干这事。很好 java.util.Optional<T> 就是那个工具类。(可恨他们又趁我没想好,就剽窃走了。)

    Optional很简单,就几个方法,看明白用法就行了。书中建议如果一个在业务层面允许为null就应该用Optional包裹它。这个需要慎重,起码你的团队都听你的才行,否则…

    第10章用Optional 取代 null

    本章内容
     null引用引发的问题,以及为什么要避免null引用
     从null到Optional:以null安全的方式重写你的域模型
     让Optional发光发热: 去除代码中对null的检查
     读取Optional中可能值的几种方法
     对可能缺失值的再思考

    10.2 Optional 类入门

    在这里插入图片描述
    Optional的思路就是把实际的(对象)保护起来,对外开放一组接口,用于满足当 值 == null值 != null 时进行某些操作。

    10.3 应用 Optional 的几种模式

    10.3.1 创建 Optional 对象

    构造函数都是 private 的不能 new

    1. 通过静态工厂方法Optional.empty,创建一个空的Optional对象:
    Optional<Car> optCar = Optional.empty();
    
    • 1
    1. 使用静态工厂方法Optional.of,依据一个非空值(为空会抛异常)创建Optional对象:
    Optional<Car> optCar = Optional.of(car);
    
    • 1
    1. 使用静态工厂方法Optional.ofNullable,你可以创建一个允许null值的Optional对象:
    Optional<Car> optCar = Optional.ofNullable(car);
    
    • 1

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

    从概念上,这与我们在第4章和第5章中看到的流的map方法很像。
    不同的是中通常会有多个元素,而Optional肚子里只可能有一个值null
    map:用给定的 lambda 处理 value 然后包裹在 Optional 中返回。比如:Optional<值>
    在这里插入图片描述

    10.3.3 使用 flatMap 链接 Optional 对象

    flatMapmap的区别就在于它会判断,如果处理的结果已经是一个Optional它就直接返回,否则同 map
    在这里插入图片描述

    在域模型中使用Optional,以及为什么它们无法序列化
    ____在代码清单10-4中,我们展示了如何在你的域模型中使用Optional,将允许缺失或者暂无定义的变量值用特殊的形式标记出来。然而,Optional类设计者的初衷并非如此,他们构思时怀揣的是另一个用例。这一点,Java语言的架构师Brian Goetz曾经非常明确地陈述过,Optional的设计初衷仅仅是要支持能返回Optional对象的语法。
    ____由于Optional类设计时就没特别考虑将其作为类的字段使用,所以它也并未实现Serializable接口。由于这个原因,如果你的应用使用了某些要求序列化的库或者框架,在域模型中使用Optional,有可能引发应用程序故障。然而,我们相信,通过前面的介绍,你已经看到用Optional声明域模型中的某些类型是个不错的主意,尤其是你需要遍历有可能全部或部分为空,或者可能不存在的对象时。如果你一定要实现序列化的域模型,作为替代方案,我们建议你像下面这个例子那样,提供一个能访问声明为Optional、变量值可能缺失的接口,代码清单如下:

    public class Person { 
    	private Car car; 
    	public Optional<Car> getCarAsOptional() { 
    		return Optional.ofNullable(car); 
    	} 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    10.3.4 默认行为及解引用 Optional 对象

    略。。。内容合并入 表10-1 Optional类的方法

    10.3.5 两个 Optional 对象的组合

    public Insurance findCheapestInsurance(Person person, Car car) { 
    	// 通过不同的保险公司提供的查询服务,获取数据
    	// 对比所有数据,得出保险最便宜的一家保险公司。
    	return cheapestCompany; 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如下的组合实现了仅当Optional<Person>Optional<Car>都有值时才会走到findCheapestInsurance(p, c)这一步。(但是从代码上看,个人觉得这操作已经有点入魔了。我不希望Optional侵入的太深。)

    public Optional<Insurance> foo( Optional<Person> person, Optional<Car> car) { 
    	return person.flatMap(p -> car.map(c -> findCheapestInsurance(p, c))); 
    }
    
    • 1
    • 2
    • 3

    10.3.6 使用 filter 剔除特定的值

    直接上代码,这里如果不满足年龄条件p.getAge() >= minAge得到的就是一个空Optional
    到了.orElse("Unknown"); 就会得到Unknown

    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("Unknown"); 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    表10-1 Optional类的方法

    方法介绍
    empty返回一个空的 Optional 实例。
    filter如果值存在且满足作为参数提供的lambda(谓词),就返回包含该值的 Optional 对象;否则返回一个空的 Optional 对象。
    map如果值存在,就对该值执行参数给定的lambdaR apply(T t),结果包装为 Optional 类型返回。
    flatMapmap。只是如果返回值已经是 Optional 则不再用 Optional封装。
    get如果该值存在,将该Optional 封装返回,否则抛NoSuchElementException
    ifPresent如果值存在时执行作为参数传进来的lambda(消费者),否则就不进行任何操作。
    isPresent如果值存在就返回 true,否则返回 false
    of将指定Optional 封装之后返回,如果该值为 null,则抛出一个 NullPointerException异常。
    ofNullable将指定Optional 封装之后返回,如果该值为 null,则返回一个空Optional对象。
    orElse就直接返回,否则返回我们指定的 other
    orElseGet就直接返回,否则执行参数给定的 lambda(生产者)生成我们想要的返回值 。
    这里的目的是借助lambda特性,达到延迟执行效果。
    orElseThrowget() ,只是可以抛自定义异常。参数是个生产者用来生成自定义异常。

    10.4 使用 Optional 的实战示例

    10.4.1 用 Optional 封装可能为 null 的值

    每次你希望安全地将可能为null的对象替换为Optional对象时,都可以考虑使用这种方法。

    HashMap<Object, Object> map = new HashMap<>();
    Optional<Object> value = Optional.ofNullable(map.get("key")); 
    
    • 1
    • 2

    这里只是用Map举例说明这种情景。实际上Java8MapgetOrDefault

    HashMap<String, String> map = new HashMap<>();
    String str = map.getOrDefault("key","默认值");
    System.out.println(str); // 默认值
    
    • 1
    • 2
    • 3

    10.4.2 异常与 Optional 的对比

    代码清单10-6 将String转换为Integer,并返回一个Optional对象。

    public static Optional<Integer> stringToInt(String s) { 
    	try { 
    		return Optional.of(Integer.parseInt(s)); 
    	} catch (NumberFormatException e) {
    		return Optional.empty(); 
    	} 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    作者:应该避免使用基础类型的Optional对象:OptionalInt、OptionalLong、OptionalDouble

    10.4.3 把所有内容整合起来

    Insurance i = new Insurance("非常保险");
    Car c = new Car(Optional.of(i));
    Person p = new Person(Optional.of(c));
    Optional<Person> optPerson = Optional.of(p);
    
    String insuranceName = optPerson
    		// flatMap 如果返回值已经是 Optional 则直接返回,否则同 map。
            .flatMap(Person::getCar)
            .flatMap(Car::getInsurance)
            // map 用给定的 lambda 处理 value 然后包裹在 Optional 中返回。
            .map(Insurance::getName)
            .orElse("无名保险");
    System.out.println(insuranceName); // 非常保险
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    10.5 小结

    这一章中,你学到了以下的内容。

    1. null引用在历史上被引入到程序设计语言中,目的是为了表示变量值的缺失。(业务上此值可以为空)
    2. Java 8中引入了一个新的类java.util.Optional<T>对存在或缺失的变量值进行建模
    3. 你可以使用静态工厂方法Optional.empty、Optional.of以及Optional.ofNullable创建Optional对象。
    4. Optional类支持多种方法,比如map、flatMap、filter它们在概念上与Stream类中对应的方法十分相似
    5. 使用Optional会迫使你更积极地引用Optional对象,以应对变量值缺失的问题,最终,你能更有效地防止代码中出现不期而至的空指针异常。 (病毒传播,慎重)
    6. 使用Optional能帮助你设计更好的API,用户只需要阅读方法签名,就能了解该方法是否接受一个Optional类型的值。 (太重,慎重)

    参考资料

    java.util.Optional<T>

  • 相关阅读:
    QEMU CAN总线
    【中间件】redis持久化方式
    关于FPGA对 DDR4 (MT40A256M16)的读写控制 I
    基于Xml方式Bean的配置-命名空间种类
    《LeetCode力扣练习》代码随想录——数组(二分查找---Java)
    window11安装wsl,wsl连接vsCode开发,安装docker、Nginx
    vue实现el-tree操作后默认展开该节点和同级节点拖拽并进行前后端排序
    SpringCloud Alibaba微服务第5章之Gateway
    Unity --- 文本的使用
    VUE + 小程序 关于前端循环上传附件页面卡死的问题
  • 原文地址:https://blog.csdn.net/jx520/article/details/125522988