Java8 In Action-3.高效 Java 8 编程(三)

3.用Optional取代null
3.1如何为缺失的值建模
采用防御式检查减少 NullPointerException

//每个null检查都会增加调用链上剩余代码的嵌套层数
public String getCarInsuranceName(Person person) {
	if (person != null) {
		Car car = person.getCar();
	if (car != null) {
		Insurance insurance = car.getInsurance();
	if (insurance != null) {
		return insurance.getName();
			}
		}
	}
return "Unknown";
}

//每 个 null 检 查 都会添加新的退出点,过多的退出语句
public String getCarInsuranceName(Person person) {
	if (person == null) {
		return "Unknown";
	}
	Car car = person.getCar();
	if (car == null) {
		return "Unknown";
	}
	Insurance insurance = car.getInsurance();
	if (insurance == null) {
		return "Unknown";
	}
	return insurance.getName();
}

2.2Optional 类入门
java.util.Optional<T>,变量存在时, Optional类只是对类简单封装。变量不存在时,缺失的值会被建模成一个 “空”的Optional对象,由方法Optional.empty()返回。Optional.empty()方法是一个静态工厂方法,它返回Optional类的特定单一实例。

/**
*使用Optional重新定义Person/Car/Insurance的数据模型,丰富你模型的语义
*/
public class Person {
private Optional<Car> car; //人可能有车,也可能没有车,因此将这个字段声明为Optional
public Optional<Car> getCar() { return car; }
}
public class Car {
private Optional<Insurance> insurance; //车可能进行了保险,也可能没有保险,所以将这个字段声明为Optional
public Optional<Insurance> getInsurance() { return insurance; }
}
public class Insurance {
private String name;//保险公司必须有名字
public String getName() { return name; }
}

特别强调,引入Optional类的意图并非要消除每一个null引用。与此相反,它的目标是帮助你更好地设计出普适的API,让程序员看到方法签名,就能了解它是否接受一个Optional的值。这种强制会让你更积极地将变量从Optional中解包出来,直面缺失的变量值。

2.3应用 Optional 的几种模式
创建 Optional 对象:

  • 声明一个空的Optional
Optional<Car> optCar = Optional.empty();
  • 依据一个非空值创建Optional
//如果car是一个null,这段代码会立即抛出一个NullPointerException,而不是等到你试图访问car的属性值时才返回一个错误。
Optional<Car> optCar = Optional.of(car);
  • 可接受null的Optional
//如果car是null,那么得到的Optional对象就是个空对象。
Optional<Car> optCar = Optional.ofNullable(car);

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

String name = null;
if(insurance != null){
name = insurance.getName();
}

//等效替换
Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);

使用 flatMap 链接 Optional 对象

public String getCarInsuranceName(Person person) {
return person.getCar().getInsurance().getName();
}

//等效替换,使用Optional获取car的Insurance名称
 public String getCarInsuranceName(Optional<Person> person) {
return person.flatMap(Person::getCar)
	.flatMap(Car::getInsurance)
	.map(Insurance::getName)
	.orElse("Unknown"); //如 果Optional的结果值为空,设置默认值
}

在这里插入图片描述
在域模型中使用Optional,以及为什么它们无法序列化

默认行为及解引用 Optional 对象

  • get()是这些方法中最简单但又最不安全的方法。如果变量存在,它直接返回封装的变量值,否则就抛一个NoSuchElementException异常。
  • orElse(T other) 它允许你在Optional对象不包含值时提供一个默认值。
  • orElseGet(Supplier<? extends T> other)是orElse方法的延迟调用版,Supplier方法只有在Optional对象不含值时才执行调用。
  • orElseThrow(Supplier<? extends X> exceptionSupplier)和get方法非常类似,它们遭遇Optional对象为空时都会抛出一个异常,但是使用orElseThrow你可以定制希望抛出的异常类型.
  • ifPresent(Consumer<? super T>)让你能在变量值存在时执行一个作为参数传入的方法,否则就不进行任何操作。

两个 Optional 对象的组合

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();
}
}

//以不解包的方式组合两个Optional对象
 public Optional<Insurance> nullSafeFindCheapestInsurance(Optional<Person> person,Optional<Car> car) {
		return person.flatMap(p -> car.map(c -> findCheapestInsurance(p, c)));
}

使用 filter 剔除特定的值
你经常需要调用某个对象的方法,查看它的某些属性。

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对象。

//找出年龄大于或者等于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("Unknown");
}

在这里插入图片描述
在这里插入图片描述

2.4使用 Optional 的实战示例
用 Optional 封装可能为 null 的值

Optional<Object> value = Optional.ofNullable(map.get("key"));

异常与 Optional 的对比

//将String转换为Integer,并返回一个Optional对象
public static Optional<Integer> stringToInt(String s) {
try {
	return Optional.of(Integer.parseInt(s));
} catch (NumberFormatException e) {
	return Optional.empty();
}
}

可以将多个类似的方法封装到一个工具类中,让我们称之为OptionalUtility。通过这种方式,你以后就能直接调用OptionalUtility.stringToInt方法,将String 转 换 为 一 个Optional<Integer> 对 象 , 而 不 再 需 要 记 得 你 在 其 中 封 装 了 笨 拙 的try/catch的逻辑了。

把所有内容整合起来

//以命令式编程的方式从属性中读取duration值
public int readDuration(Properties props, String name) {
String value = props.getProperty(name);
if (value != null) {
try {
int i = Integer.parseInt(value);
if (i > 0) {
return i;
}
} catch (NumberFormatException nfe) { }
}
return 0;
}

//使用Optional从属性中读取duration
public int readDuration(Properties props, String name) {
return Optional.ofNullable(props.getProperty(name))
	.flatMap(OptionalUtility::stringToInt)
	.filter(i -> i > 0)
	.orElse(0);
}

注意到使用Optional和Stream时的那些通用模式了吗?它们都是对数据库查询过程的反
思,查询时,多种操作会被串接在一起执行。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值