Java8实战-总结41

用Optional取代null

Optional 类入门

Java 8中引入了一个新的类java.util.Optional<T>这是一个封装Optional值的类。举例来说,使用新的类意味着,如果你知道一个人可能有也可能没有车,那么Person类内部的car变量就不应该声明为Car,遭遇某人没有车时把null引用赋值给它,而是应该像下图直接将其声明为Optional<Car>类型。
在这里插入图片描述
变量存在时,Optional类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空”的Optional对象,由方法Optional.empty()返回。Optional.empty()方法是一个静态工厂方法,它返回Optional类的特定单一实例。null引用和Optional.empty()有什么本质的区别吗?从语义上,可以把它们当作一回事儿,但是实际中它们之间的差别非常大:如果尝试解引用一个 null ,一定会触发 NullPointerException ,不过使用Optional.empty()就完全没事儿,它是Optional类的一个有效对象,多种场景都能调用,非常有用。

使用Optional而不是null的一个非常重要而又实际的语义区别是,第一个例子中,在声明变量时使用的是Optional<Car>类型,而不是Car类型,这句声明非常清楚地表明了这里发生变量缺失是允许的。与此相反,使用Car这样的类型,可能将变量赋值为null,这意味着需要独立面对这些,只能依赖对业务模型的理解,判断一个null是否属于该变量的有效范畴。

牢记上面这些原则,现在可以使用Optional类对最初的代码进行重构,结果如下:

public class Person { 
	private Optional<Car> car; 
	public Optional<Car> getCar() { return car; } 
}

public class Car { 
	private Optional<Insurance> insurance; 
	public Optional<Insurance> getInsurance() { return insurance; } 
} 

public class Insurance { 
	private String name; 
	public String getName() { return name; }
} 

代码中person引用的是Optional<Car>,而car引用的是Optional<Insurance>,这种方式非常清晰地表达了模型中一个person可能拥有也可能没有car的情形,同样,car可能进行了保险,也可能没有保险。与此同时,insurance公司的名称被声明成String类型,而不是Optional<String>,这非常清楚地表明声明为insurance公司的类型必须提供公司名称。使用这种方式,一旦解引用insurance公司名称时发生NullPointerException,就能非常确定地知道出错的原因,不再需要为其添加null的检查,因为null的检查只会掩盖问题,并未真正地修复问题。

insurance公司必须有个名字,所以,如果遇到一个公司没有名称,需要调查数据出了什么问题,而不应该再添加一段代码,将这个问题隐藏。

在代码中始终如一地使用Optional,能非常清晰地界定出变量值的缺失是结构上的问题,还是算法上的缺陷,抑或是数据中的问题。另外,引入Optional类的意图并非要消除每一个null引用。与此相反,它的目标是帮助更好地设计出普适的API,让程序员看到方法签名,就能了解它是否接受一个Optional的值。这种强制可以帮助更积极地将变量从Optional中解包出来,直面缺失的变量值。

应用 Optional 的几种模式

创建 Optional 对象

使用Optional之前,首先需要学习的是如何创建Optional对象。完成这一任务有多种方法。

  1. 声明一个空的Optional
    可以通过静态工厂方法Optional.empty,创建一个空的Optional对象:
Optional<Car> optCar = Optional.empty(); 
  1. 依据一个非空值创建Optional
    还可以使用静态工厂方法Optional.of,依据一个非空值创建一个Optional对象:
Optional<Car> optCar = Optional.of(car); 

如果car是一个null,这段代码会立即抛出一个NullPointerException,而不是等到试图访问car的属性值时才返回一个错误。

  1. 可接受null的Optional

最后,使用静态工厂方法Optional.ofNullable,可以创建一个允许null值的Optional对象:

Optional<Car> optCar = Optional.ofNullable(car); 

如果carnull,那么得到的Optional对象就是个空对象。

还需要获取Optional变量中的值,Optional提供了一个get方法,它能非常精准地完成这项工作,不过get方法在遭遇到空的Optional对象时也会抛出异常,所以不按照约定的方式使用它,又会让我们再度陷入由null引起的代码维护的梦魇。因此,首先从无需显式检查的Optional值的使用入手,这些方法与Stream中的某些操作极其相似。

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

从对象中提取信息是一种比较常见的模式。比如,可能想要从insurance公司对象中提取公司的名称。提取名称之前,需要检查insurance对象是否为null,代码如下所示:

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

为了支持这种模式,Optional提供了一个map方法。它的工作方式如下:

Optional<Insurance> optInsurance = Optional.ofNullable(insurance); 
Optional<String> name = optInsurance.map(Insurance::getName);

从概念上,这与流的map方法相差无几。map操作会将提供的函数应用于流的每个元素。可以把Optional对象看成一种特殊的集合数据,它至多包含一个元素。如果Optional包含一个值,那函数就将该值作为参数传递给map,对该值进行转换。如果Optional为空,就什么也不做。下图对这种相似性进行了说明,展示了把一个将正方形转换为三角形的函数,分别传递给正方形和Optional正方形流的map方法之后的结果。
在这里插入图片描述
这看起来挺有用,但是怎样才能应用起来,重构之前的代码呢?前文的代码里用安全的方式链接了多个方法。

public String getCarInsuranceName(Person person) { 
	return person.getCar().getInsurance().getName(); 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值