在 Java 语言开发中,可能大多数程序员遇到最多的异常就是 NullPointException 空指针异常了。这个当初语言的开发者“仅仅由于这样实现起来更容易”而容许空引用所带来的代价是很是惨痛的。而咱们开发者不得不使用多重 if 嵌套判断来规避 NPE 或者经过多个 if 结合 return 语句来终止程序。且看一个例子java
假如须要处理下面的嵌套对象,这是一个用于汽车、汽车保险的客户。程序员
public class Person { private Car car; public Car getCar() { return car; } } public class Car { private Insurance insurance; public Insurance getInsurance() { return insurance; } } public class Insurance { private String name; public String getName() { return name; } }
那么下面的代码会存在怎样的问题呢?数据库
public String getCarInsuranceNames(Person person) { return person.getCar().getInsurance().getName(); }
没错,当这我的没有车 / 他的车没有上保险时,代码会抛出 NPE。或者说这我的根本就是 null,也会直接抛出异常。咱们常见的做法就是在每次 get 方法以后,进行 if 判断,增长代码的健壮性。但是这样代码会显得十分臃肿。Java 语言的开发者们也在关注着这些问题。所以在 Java8 提供了新的 API:java.util.Optional 用来优雅的处理 null。接下来就请读者和我一块儿揭开 Optional 神秘的面纱吧!安全
PS:Optional 类提供的不少 API 结合 Lambda 表达式食用更佳,另外还有不少 API 和 Stream 流中同名 API 的思想基本一致。所以建议读者先行了解这两个知识点,能够在个人博客 Java8新特性 标签下学习app
声明:本文首发于博客园,做者:后青春期的Keats;地址:https://www.cnblogs.com/keatsCoder/ 转载请注明,谢谢!框架
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; } }
注意:对于保险来讲,咱们从逻辑层面限定每一个保险公司都有名称,若是没有,那通常是数据出了问题而非代码的问题,开发者应该着手去寻找为何数据库存在名字为空的保险公司。而不是这里抛出 NPE,故而咱们不用将 Insurance 的 name 字段使用 Optional 包裹学习
经过上面的代码,咱们已经将对象由 Optional 所包裹了,那接下来咱们该如何使用它呢?ui
Optional<Object> empty = Optional.empty();
Optional.empty(); 该方法返回一个空对象,
Optional<Car> car = Optional.of(c);
Optional.of(T t); 方法会返回一个 Optional
Optional
为了不在建立 Optional 对象时,因为源对象为空而引起的 NPE,该类还提供了 ofNullable 方法,当参数为 null 时,返回 Optional.empty()。内部的 API 是这样的
public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); }
public <U> Optional<U> map(Function<? super T, ? extends U> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) { return empty(); } else { return Optional.ofNullable(mapper.apply(value)); } }
Optional 类提供 map 方法,接收一个函数式接口 Function 的实现类,若是调用者是空的,则返回 empty(),不然对 Optional 中的对象 value 调用 Function 实现类中的 apply() 方法,再包装成 Optional 返回。能够用下面的图直观的看到 map 执行的过程:
请注意,在 map 执行完 apply 方法拿到返回值以后,会主动将返回值再次包裹成 Optional 对象。所以咱们若是按照下面的方式改造咱们以前的方法,编译是没法经过的:
person.map(Person::getCar).map(Car::getInsurance).map(Insurance::getName);
咱们来分析一下: person.map(Person::getCar) 改造后的 person 类中, getCar 方法返回 Optional
幸运的是,和 Stream 同样,Optional 也提供了扁平化流的方法 flatMap()。且看源码
public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) { return empty(); } else { @SuppressWarnings("unchecked") Optional<U> r = (Optional<U>) mapper.apply(value); return Objects.requireNonNull(r); } }
flatMap() 比 map() 方法多了一个执行完后将嵌套 Optional 强转成 Optional 的操做,避免了流不能继续使用的尴尬处境。所以,咱们能够将获取保险公司名称的方法改形成下面这样:
public String getCarInsuranceName(Optional<Person> person) { return person.flatMap(Person::getCar) .flatMap(Car::getInsurance) .map(Insurance::getName) .orElse("Unknown"); }
其中 orElse() 方法表示当最终 Optional 包裹的对象仍是空时,返回的默认值
PS:因为 Optional 并无实现序列化接口,所以若是你的项目中使用了某些要求序列化的框架,而且在某个类中使用 Optional 包裹了字段。可能会由序列化引起程序故障。
经过 get() 方法获取变量,若是变量存在就直接获得该变量,不然抛出一个 throw new NoSuchElementException("No value present"); 异常。通常不建议使用该方法毕竟直接用 get() 方法了,还要整 Optional 这些花里胡哨的干啥呢
在对象为 null 时提供一个默认值
在对象为 null 经过调用 supplier 提供者接口的实现,返回一个值
在对象为 null 抛出一个可定制的异常信息,能够用来抛出项目中的自定义异常,以便全局异常捕获器抓取及响应数据
当对象不为 null 时,执行消费者操做。为 null 时啥也不干
咱们经常调用某个对象的某个方法去判断其属性。为了安全操做。首先须要对该对象进行非空校验。例如要检查保险公司名称是否为 Keats,须要这么写
if(i != null && "Keats".equals(i.getName())){ System.out.println("yes"); }
如今咱们能够这么写
Optional<Insurance> insurance = Optional.ofNullable(i); insurance.filter(in -> "Keats".equals(in.getName())).ifPresent(in -> System.out.println("yes"));
先看 filter 的源码
public Optional<T> filter(Predicate<? super T> predicate) { Objects.requireNonNull(predicate); if (!isPresent()) { return this; } else { return predicate.test(value) ? this : empty(); } }
首先第一步检查了谓词实现非空,第二步判断 Optional 中的对象若是为空则返回空 Optional,若是不为空执行谓词方法,条件成立则返回该对象。不然返回空 Optional。即仅当 Optional 中对象不为 null 且符合条件时,返回该对象以后经过 ifPresent() 方法执行接下来的逻辑。很是方便易懂
Optional 还提供了一些基础类型对象对应的类,如 OptionalInt、OptionalLong 同 Stream 流同样,采用基本操做类型处理数据,避免了自动拆装箱带来的性能损失。但却牺牲了 map、flatMap、filter 方法。开发中需酌情使用
码字不易,若是你以为读完之后有收获,不妨点个推荐让更多的人看到吧!