Optional 类解决空指针异常

共 6176字,需浏览 13分钟

 ·

2021-05-05 11:19

前言

空指针异常是导致 Java 应用程序失败的最常见原因。以前,为了解决空指针异常,Google 公司著名的 Guava 项目引入了 Optional 类,Guava 通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到 Google Guava 的启发,Optional 类已经成为 Java8 类库的一部分。Optional 实际上是个容器:它可以保存类型 T 的值,或者仅仅保存 null。Optional 提供很多有用的方法,这样我们就不用显式进行空值检测。

Optional 范例

构造方式

Optional 的三种构造方式:Optional.of(obj),Optional.ofNullable(obj) 和 Optional.empty()。

  • Optional.of(),依据一个非空值创建 Optional

// 如果 car 是一个 null, 这段代码会立即抛出 
// NullPointException, 而不是等到试图访问 car 的属性值时才返回一个错误.
Optional<Integer> optCar = Optional.of(car);
  • Optional.ofNullable(),可接受 null 的 Optional(实现序列化的域模型时使用)

// 创建一个允许 null 值的 Optional 对象, 
// 如果 car 是 null, 那么得到的 Optional 对象就是个空对象.
Optional<Integer> optCar = Optional.ofNullable(car);
  • Optional.empty(),所有 null 包装成的 Optional 对象。

// 声明一个空的 Optional
Optional<Integer> empty = Optional.empty();

isPresent()/ifPresent(Consumer consumer)

isPresent(),判断值是否存在。

Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);

// isPresent 判断值是否存在
System.out.println(optional1.isPresent() == true);
System.out.println(optional2.isPresent() == false);

ifPresent(Consumer consumer):如果 option 对象保存的值不是 null,则调用 consumer 对象,否则不调用。

// 如果不是 null, 调用 Consumer; 如果 null, 不调用 Consumer
optional1.ifPresent(new Consumer<Integer>() {
    @Override
    public void accept(Integer t
{
        System.out.println("value is" + t);
    }
});

// Lambda 表达式写法
optional1.ifPresent(t -> System.out.println("value is" + t));

orElse(value)/orElseGet(Supplier supplier)

orElse(value):如果 optional 对象保存的值不是 null,则返回原来的值,否则返回 value。

Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);

System.out.println(optional1.orElse(1000) == 1);// true
System.out.println(optional2.orElse(1000) == 1000);// true

orElseGet(Supplier supplier):功能与 orElse 一样,只不过 orElseGet 参数是一个对象,即无则由函数来产生。

Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);

System.out.println(optional1.orElseGet(() -> {
    return 1000;
}) == 1);
// true

System.out.println(optional2.orElseGet(() -> {
    return 1000;
}) == 1000);
// true

orElseThrow()

orElseThrow():值不存在则抛出异常,存在则什么不做,有点类似 Guava 的 Precoditions。

Optional<Integer> optional = Optional.ofNullable(null);

optional.orElseThrow(() -> {
    throw new NullPointerException();
});

filter(Predicate)

filter(Predicate):判断 Optional 对象中保存的值是否满足 Predicate,并返回新的 Optional。

Optional<Integeroptional = Optional.ofNullable(1);

Optional<Integer> filter = optional.filter((a) -> a == 1);

map(Function)/flatMap(Function)

map(Function) 如果有值,则对其执行调用 mapping 函数得到返回值,否则返回空 Optional。如果返回值不为 null,则创建包含 mapping 返回值的 Optional 作为 map 方法返回值,否则返回空 Optional。

Optional<String> upperName = name.map((value) -> value.toUpperCase());
System.out.println(upperName.orElse("No value found"));

flatMap(Function) 如果有值,为其执行 mapping 函数返回 Optional 类型返回值,否则返回空 Optional。flatMap 与 map(Funtion)方法类似,区别在于 flatMap 中的 mapper 返回值必须是 Optional。调用结束时,flatMap 不会对结果用 Optional 封装。[P.S. 参考博文 [3]:Cascading Optional Objects Using the flatMap Method]

upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
System.out.println(upperName.orElse("No value found"));

Optional 类的链式方法

错误示范 / 正确示范

  • 示范(一)

// ** 错误示范
Optional<User> user = ...... 
if (user.isPresent()) {
    return user.getOrders();
else {
    return Collections.emptyList();
}
// 二者实质上是没有任何分别
User user = .....
if (user != null) {
    return user.getOrders();
else {
    return Collections.emptyList();
}

// ** 正确示范
return user.map(u -> u.getOrders()).orElse(Collections.emptyList())

// map 是可能无限级联的, 比如再深一层, 获得用户名的大写形式
return user.map(u -> u.getUsername())
           .map(name -> name.toUpperCase())
           .orElse(null);
  • 示范(二)

// ** 错误示范
String version = "UNKNOWN";
if(computer != null){
  Soundcard soundcard = computer.getSoundcard();
  if(soundcard != null){
    USB usb = soundcard.getUSB();
    if(usb != null){
      version = usb.getVersion();
    }
  }
}

// ** 正确示范
String version = computer.map(Computer::getSoundcard)
                  .map(Soundcard::getUSB)
                  .map(USB::getVersion)
                  .orElse("UNKNOWN");

参考博文

[1]. Java 8 Optional 类深度解析
[2]. 使用 Java8 Optional 的正确姿势
[3]. Tired of Null Pointer Exceptions? Consider Using Java SE 8’s Optional!

source:https://morning-pro.github.io/archives/8eb6feba.html

喜欢,在看

浏览 15
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报