public static String getCarInsuranceName(Person person) {
// return person.getCar().getInsurance().getName();
//null-安全的第一种尝试:深层质疑
if (person != null) {
Car car = person.getCar();
if (car != null) {
Insurance insurance = car.getInsurance();
if (insurance != null) {
return insurance.getName();
}
}
}
return "Unknown";
}
深层质疑”,原因是它不断重复着一种模式:每次你不确定一
个变量是否为null时,都需要添加一个进一步嵌套的if块,也增加了代码缩进的层数。很明显,
这种方式不具备扩展性,同时还牺牲了代码的可读性。
第二种尝试
//null-安全的第二种尝试:过多的退出语句
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();
第二种尝试中,你试图避免深层递归的if语句块,采用了一种不同的策略: 每次你遭遇null
变量,都返回一个字符串常量“Unknown”。然而,这种方案远非理想,现在这个方法有了四个
截然不同的退出点,使得代码的维护异常艰难。更糟的是,发生null时返回的默认值,即字符
串“Unknown”在三个不同的地方重复出现——出现拼写错误的概率不小!当然,你可能会说,
我们可以用把它们抽取到一个常量中的方式避免这种问题。
使用Optional重新定义Person/Car/Insurance的数据模型
变量存在时, Optional类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空”
的Optional对象,由方法Optional.empty()返回。 Optional.empty()方法是一个静态工厂
方法,它返回Optional类的特定单一实例。你可能还有疑惑, null引用和Optional.empty()
有什么本质的区别吗?从语义上,你可以把它们当作一回事儿,但是实际中它们之间的差别非常
大 : 如 果 你 尝 试 解 引 用 一 个 null , 一 定 会 触 发 NullPointerException , 不 过 使 用
Optional.empty()就完全没事儿,它是Optional类的一个有效对象,多种场景都能调用,非
常有用。
Optional<Person> optPerson = Optional.of(person);
Optional<String> name =
optPerson.map(Person::getCar)
.map(Car::getInsurance)
.map(Insurance::getName);
不幸的是,这段代码无法通过编译。为什么呢? optPerson是Optional<Person>类型的
变量, 调用map方法应该没有问题。但getCar返回的是一个Optional<Car>类型的对象(如代
码清单10-4所示),这意味着map操作的结果是一个Optional<Optional<Car>>类型的对象。因
此,它对getInsurance的调用是非法的,因为最外层的optional对象包含了另一个optional
对象的值,而它当然不会支持getInsurance方法。
所以,我们该如何解决这个问题呢?让我们再回顾一下你刚刚在流上使用过的模式:
flatMap方法。使用流时, flatMap方法接受一个函数作为参数,这个函数的返回值是另一个流。
这个方法会应用到流中的每一个元素,最终形成一个新的流的流。但是flagMap会用流的内容替
换每个新生成的流。换句话说,由方法生成的各个流会被合并或者扁平化为一个单一的流。这里
你希望的结果其实也是类似的,但是你想要的是将两层的optional合并为一个。