Optional类详解

简述

  Optional类是java8中引入的一个非常有用的类,主要用处是解决编程中的空指针异常,本质上,这是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空。Optional 是 Java 实现函数式编程的强劲一步,并且帮助在范式中实现。

空指针异常的麻烦

  在java8之前,任何访问对象方法或属性的调用都有可能导致空指针异常,例如:

String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();

  这行代码如果我们需要确保不触发异常,我们就要写成:

if (user != null) {
    Address address = user.getAddress();
    if (address != null) {
        Country country = address.getCountry();
        if (country != null) {
            String isocode = country.getIsocode();
            if (isocode != null) {
                isocode = isocode.toUpperCase();
            }
        }
    }
}

创建Optional实例

  使用empty()创建空实例

Optional<User> emptyOpt = Optional.empty();

  尝试访问emptyOpt变量的值会导致NoSuchElementException

  使用of()或ofNullable()方法创建包含值的Optional,不同之处在于:如果你把 null 值作为参数传递进去,of() 方法会抛出 NullPointerException

Optional<User> opt1 = Optional.of(user);
Optional<User> opt1 = Optional.ofNullable(user);

访问Optional对象的值

  使用get()方法获取Optional的值

Optional<User> opt1 = Optional.of(user);
User tmp = opt1.get();

  get方法会在值为null的时候抛出异常,我们可以用ifPresent()方法验证值是否为空

返回默认值

  orElse()方法可以在创建实例时就指定返回的默认值

User user = null;
User user2 = new User("anna@gmail.com", "1234");
User result = Optional.ofNullable(user).orElse(user2);

  因为user是空,所以此时我们想获取result的值的话会得到user2

  我们也可以使用orElseGet(),如果没有值,它会执行作为参数传入的 Supplier(供应者) 函数式接口,并将返回其执行结果

User result = Optional.ofNullable(user).orElseGet( () -> user2);

  不同的是使用orElse的话无论创建的值是否为空,都会orElse里的代码,如果里面有创建新的实例,这会对性能产生很大影响

  另外我们可以使用orElseThrow(),它会在对象为空的时候抛出异常,而不是返回备选的值

User result = Optional.ofNullable(user)
        .orElseThrow( () -> new IllegalArgumentException());

  此时如果user为空,会抛出我们定义的异常IllegalArgumentException

转换值

  我们可以使用map方法,将 Optional里的元素进行转换

User user = new User("anna@gmail.com", "1234");
String email = Optional.ofNullable(user)
        .map(u -> u.getEmail()).orElse("default@gmail.com");

  注意无论是user为null,还是user里的Email为空,都会触发返回orElse里的默认值,这也就Optional的map操作的链式调用的精髓,我们可以不断的接着写map,无需一次次地判断是否为空

  注意如果当对象发生Optinoal嵌套的时候,我们需要使用flatMap方法,详细请看:https://blog.csdn.net/qq_35634181/article/details/101109300

过滤值

  filter()接受一个Predicate参数,返回测试结果为 true 的值

  用法和stream的filter一模一样,例如我们可以检查用户的邮箱是否带@

User user = new User("anna@gmail.com", "1234");
Optional<User> result = Optional.ofNullable(user)
        .filter(u -> u.getEmail() != null && u.getEmail().contains("@"));

对原来if的改进

  对于User类我们对其进行重构,使其getter方法返回Optional类

public class User {
    private Address address;

    public Optional<Address> getAddress() {
        return Optional.ofNullable(address);
    }

    // ...
}
public class Address {
    private Country country;

    public Optional<Country> getCountry() {
        return Optional.ofNullable(country);
    }

    // ...
}
View Code

  上面的嵌套结构可以用下面的图来表示:

  之前冗杂的if判断我们就可以改成这样:

String result = Optional.ofNullable(user)
        .flatMap(u -> u.getAddress())
        .flatMap(a -> a.getCountry())
        .map(c -> c.getIsocode())
        .orElse("default");

  也可以简写成这样:

String result = Optional.ofNullable(user)
  .flatMap(User::getAddress)
  .flatMap(Address::getCountry)
  .map(Country::getIsocode)
  .orElse("default");

Reference

  https://blog.csdn.net/wwe4023/article/details/80760416

  https://blog.csdn.net/qq_35634181/article/details/101109300

posted @ 2022-02-28 21:37  艾尔夏尔-Layton  阅读(1145)  评论(0编辑  收藏  举报