• Java8实战-总结42


    用Optional取代null

    应用 Optional 的几种模式

    默认行为及解引用 Optional 对象

    采用orElse方法读取这个变量的值,使用这种方式还可以定义一个默认值,遭遇空的Optional变量时,默认值会作为该方法的调用返回值。Optional类提供了多种方法读取Optional实例中的变量值。

    • get()是这些方法中最简单但又最不安全的方法。如果变量存在,它直接返回封装的变量值,否则就抛出一个NoSuchElementException异常。所以,除非非常确定Optional变量一定包含值,否则使用这个方法是个相当糟糕的主意。此外,这种方式即便相对于嵌套式的null检查,也并未体现出多大的改进。
    • orElse(T other)允许在Optional对象不包含值时提供一个默认值。
    • orElseGet(Supplier other)orElse方法的延迟调用版,Supplier方法只有在Optional对象不含值时才执行调用。如果创建默认值是件耗时费力的工作,应该考虑采用这种方式(借此提升程序的性能),或者需要非常确定某个方法仅在Optional为空时才进行调用,也可以考虑该方式(这种情况有严格的限制条件)。
    • orElseThrow(Supplier exceptionSupplier)get方法非常类似,它们遭遇Optional对象为空时都会抛出一个异常,但是使用orElseThrow可以定制希望抛出的异常类型。
    • ifPresent(Consumer)能在变量值存在时执行一个作为参数传入的方法,否则就不进行任何操作。

    Optional类和Stream接口的相似之处,远不止mapflatMap这两个方法。还有第三个方法filter,它的行为在两种类型之间也极其相似。

    两个 Optional 对象的组合

    现在,假设有这样一个方法,它接受一个Person和一个Car对象,并以此为条件对外部提供的服务进行查询,通过一些复杂的业务逻辑,试图找到满足该组合的最便宜的保险公司:

    public Insurance findCheapestInsurance(Person person, Car car) { 
    		// 不同的保险公司提供的查询服务
    		// 对比所有数据
    	return cheapestCompany; 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    还假设想要该方法的一个null安全的版本,它接受两个Optional对象作为参数,返回值是一个Optional对象,如果传入的任何一个参数值为空,它的返回值亦为空。Optional类还提供了一个isPresent方法,如果Optional对象包含值,该方法就返回true,所以第一想法可能是通过下面这种方式实现该方法:

    public Optional<Insurance> nullSafeFindCheapestInsurance(Optional<Person> person, Optional<Car> car) { 
    	if (person.isPresent() && car.isPresent()) {
    		return Optional.of(findCheapestInsurance(person.get(), car.get())); 
    	} else { 
    		return Optional.empty(); 
    	} 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这个方法具有明显的优势,从它的签名就能非常清楚地知道无论是person还是car,它的值都有可能为空,出现这种情况时,方法的返回值也不会包含任何值。不幸的是,该方法的具体实现和之前曾经实现的null检查太相似了:方法接受一个Person和一个Car对象作为参数,而二者都有可能为null。利用Optional类提供的特性,可以用更好的方式来实现这个方法。

    以不解包的方式组合两个Optional对象
    
    结合本节中介绍的map和flatMap方法,用一行语句重新实现之前出现的nullSafeFindCheapestInsurance()方法。
    
    答案:可以像使用三元操作符那样,无需任何条件判断的结构,以一行语句实现该方法,
    代码如下。
    
    public Optional nullSafeFindCheapestInsurance(Optional person, Optional car) { 
    		return person.flatMap(p -> car.map(c -> findCheapestInsurance(p, c))); 
    } 
    这段代码中,对第一个Optional对象调用flatMap方法,如果它是个空值,传递给它
    的Lambda表达式不会执行,这次调用会直接返回一个空的Optional对象。反之,如果person
    对象存在,这次调用就会将其作为函数Function的输入,并按照与flatMap方法的约定返回
    一个Optional对象。这个函数的函数体会对第二个Optional对象执行map操
    作,如果第二个对象不包含car,函数Function就返回一个空的Optional对象,整个
    nullSafeFindCheapestInsuranc方法的返回值也是一个空的Optional对象。最后,如果
    person和car对象都存在,作为参数传递给map方法的Lambda表达式能够使用这两个值安全
    地调用原始的findCheapestInsurance方法,完成期望的操作。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    Optional类和Stream接口的相似之处远不止mapflatMap这两个方法。还有第三个方法filter,它的行为在两种类型之间也极其相似。

    使用 filter 剔除特定的值

    经常需要调用某个对象的方法,查看它的某些属性。比如,可能需要检查保险公司的名称是否为“Cambridge-Insurance”。为了以一种安全的方式进行这些操作,首先需要确定引用指向的Insurance对象是否为null,之后再调用它的getName方法,如下所示:

    Insurance insurance = ...; 
    if(insurance != null && "CambridgeInsurance".equals(insurance.getName())) { 
    	System.out.println("ok");
    } 
    
    • 1
    • 2
    • 3
    • 4

    使用Optional对象的filter方法,这段代码可以重构如下:

    Optional<Insurance> optInsurance = ...; 
    optInsurance.filter(insurance -> "CambridgeInsurance".equals(insurance.getName())) '		
    			.ifPresent(x -> System.out.println("ok")); 
    
    • 1
    • 2
    • 3

    filter方法接受一个谓词作为参数。如果Optional对象的值存在,并且它符合谓词的条件,filter方法就返回其值;否则它就返回一个空的Optional对象。如果还记得可以将Optional看成最多包含一个元素的Stream对象,这个方法的行为就非常清晰了。如果Optional对象为空,它不做任何操作,反之,它就对Optional对象中包含的值施加谓词操作。如果该操作的结果为true,它不做任何改变,直接返回该Optional对象,否则就将该值过滤掉,将Optional的值置空。

    对Optional对象进行过滤
    
    假设在Person/Car/Insurance 模型中,Person还提供了一个方法可以取得
    Person对象的年龄,使用下面的签名改写之前的getCarInsuranceName方法:
    public String getCarInsuranceName(Optional person, int minAge) 
    找出年龄大于或者等于minAge参数的Person所对应的保险公司列表。
    
    答案:可以对Optional封装的Person对象进行filter操作,设置相应的条件谓词,
    即如果person的年龄大于minAge参数的设定值,就返回该值,并将谓词传递给filter方法,
    代码如下所示。
    public String getCarInsuranceName(Optional person, int minAge) { 
    	return person.filter(p -> p.getAge() >= minAge) 
    						 .flatMap(Person::getCar) 
    			  			 .flatMap(Car::getInsurance) 
    					     .map(Insurance::getName) 
    						 .orElse("Unknown"); 
    } 
    下表对`Optional`类中的方法进行了分类和概括。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    在这里插入图片描述

  • 相关阅读:
    CLion 配置 Qt 开发环境
    R语言—数据结构
    26-sparkstreaming
    windows server 2012 R2的C盘空间满了,但是找不到大文件的两种原因
    智能家居相关企业达2万余家,湖南智能家居发展将进入快车道
    Java多线程探究【二线程状态】
    Java 面向对象编程
    ECC有关DER文件的解析(Java)
    数据库设计 Relational Language
    深度学习(PyTorch)——生成对抗网络(GAN)
  • 原文地址:https://blog.csdn.net/weixin_42583701/article/details/133895515