Java8实战-总结42

用Optional取代null

应用 Optional 的几种模式

默认行为及解引用 Optional 对象

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

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

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

两个 Optional 对象的组合

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

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

还假设想要该方法的一个null安全的版本,它接受两个Optional对象作为参数,返回值是一个Optional<Insurance>对象,如果传入的任何一个参数值为空,它的返回值亦为空。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(); 
	} 
} 

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

以不解包的方式组合两个Optional对象

结合本节中介绍的map和flatMap方法,用一行语句重新实现之前出现的nullSafeFindCheapestInsurance()方法。

答案:可以像使用三元操作符那样,无需任何条件判断的结构,以一行语句实现该方法,
代码如下。

public Optional<Insurance> nullSafeFindCheapestInsurance(Optional<Person> person, Optional<Car> car) { 
		return person.flatMap(p -> car.map(c -> findCheapestInsurance(p, c))); 
} 
这段代码中,对第一个Optional对象调用flatMap方法,如果它是个空值,传递给它
的Lambda表达式不会执行,这次调用会直接返回一个空的Optional对象。反之,如果person
对象存在,这次调用就会将其作为函数Function的输入,并按照与flatMap方法的约定返回
一个Optional<Insurance>对象。这个函数的函数体会对第二个Optional对象执行map操
作,如果第二个对象不包含car,函数Function就返回一个空的Optional对象,整个
nullSafeFindCheapestInsuranc方法的返回值也是一个空的Optional对象。最后,如果
person和car对象都存在,作为参数传递给map方法的Lambda表达式能够使用这两个值安全
地调用原始的findCheapestInsurance方法,完成期望的操作。

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

使用 filter 剔除特定的值

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

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

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

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

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

对Optional对象进行过滤

假设在Person/Car/Insurance 模型中,Person还提供了一个方法可以取得
Person对象的年龄,使用下面的签名改写之前的getCarInsuranceName方法:
public String getCarInsuranceName(Optional<Person> person, int minAge) 
找出年龄大于或者等于minAge参数的Person所对应的保险公司列表。

答案:可以对Optional封装的Person对象进行filter操作,设置相应的条件谓词,
即如果person的年龄大于minAge参数的设定值,就返回该值,并将谓词传递给filter方法,
代码如下所示。
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"); 
} 
下表对`Optional`类中的方法进行了分类和概括。

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值