美文网首页
Java8之Optional类,巧解NPE

Java8之Optional类,巧解NPE

作者: 程序员Mark_Chou | 来源:发表于2021-01-02 19:14 被阅读0次

    NullPointerException——空指针异常是程序中常见异常之一,也是导致程序运行失败的常见异常。以前,为了防止出现null,我们常在代码中使用if…else…做防御性检查,后来Guava为了解决上述方法造成的代码污染引入了Optional类。

    Java8借鉴Guava的Optional也加入了同名的Optional类,Optional类提供了很多实用的方法,借此可以避免显示的空指针判断,从而避免NullPointerException

    常见方法

    下面逐一讲解Optional类提供的方法。

    of方法
    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }
    

    Optional中的构造方法都是private的,外部无法使用new方式创建Optional对象,但Optional类提供三个方法用来获取Optional的实例对象。of方法是其中之一,用来创建一个包装对象值非空的Optional实例对象,若包装对象值为空则抛出NullPointerException异常。

    // user若为null则会抛出NullPointerException异常
    Optional<User> optional_user = Optional.of(user);
    
    ofNullable方法
    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }
    

    获取Optional实例对象的方法之一,返回一个允许包装对象值为空的Optional实例对象。ofNullableof方法的区别就在是否允许包装对象为空。

    // 可以为空,且optional_user1 == optional_user2为true
    Optional<User> optional_user1 = Optional.ofNullable(null);
    Optional<User> optional_user2 = Optional.ofNullable(null);
    
    empty方法
    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }
    

    获取Optional实例对象的方法之一,创建一个包装对象值为空的Optional实例对象。

    // 返回包装对象值为空的Optional实例对象
    Optional<User> optional_user = Optional.empty();
    
    isPresent方法
    public boolean isPresent() {
        return value != null;
    }
    

    判断包装对象值是否为空。

    // 这里的user不为null
    Optional<User> optional_user1 = Optional.ofNullable(user);
    Optional<User> optional_user2 = Optional.ofNullable(null);
    
    // 返回true
    optional_user1.isPresent();
    // 返回false
    optional_user2.isPresent();
    
    ifPresent方法
    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }
    

    判断包装对象值是否为空,如果包装对象的值为空则调用Consumer对象的accept()方法,不为空则不调用。

    Optional.ofNullable(user).ifPresent(() -> {
        // 包装对象值为空执行的代码
        ……
    });
    
    get方法
    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }
    

    返回包装对象的值,若为空则抛出NoSuchElementException异常。

    orElse方法
    public T orElse(T other) {
        return value != null ? value : other;
    }
    

    若包装对象不是null则返回包装对象的实际值,若为null则返回指定值,即返回传入的参数。

    Optional.ofNullable(user).orElse(new User("zhangsan", 20));
    
    orElseGet方法
    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }
    

    作用同orElse方法,只不过orElseGet的入参是Supplier对象,当Optional包装对象为null时返回指定值,返回值由Supplierget方法产生。

    // name是一个String对象
    Optional.ofNullable(name).orElseGet(() -> "zhangsan");
    
    orElseThrow方法
    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }
    

    作用同orElseGet类似,当包装对象不为空时返回包装对象的值,为空时返回一个Throwable异常。

    filter方法
    public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }
    

    过滤Optional的包装对象,当包装对象满足Predicate的条件时返回该Optional实例,否则返回包装对象为nullOptional实例。

    // 过滤name为zhangsan的User,当不存在时打印提示信息
    Optional.ofNullable(user)
        .filter(u -> u.getName.equals("zhangsan"))
        .ifPresent(() -> System.out.println("There is no zhangsan"));
    
    map方法
    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));
        }
    }
    

    map方法对包装对象进行函数运算(即调用Functionapply方法)后返回一个新的Optional实例对象,新Optional实例的包装对象可以时任意类型(不一定与原包装对象的类型保持一致)。

    如果包装对象为null则依然返回一个包装对象为nullOptional实例,map可以无限级调用。

    // 返回一个Optional<String>
    Optional.of(user).map(u -> u.getName());
    
    flatMap方法
    public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }
    

    功能同map方法,二者的区别在mapperapply方法的返回值类型不同,map方法中的mapper返回值可以是任意类型,而flatMap中的mapper的返回值是Optional类型,map方法会自动封装返回一个Optional

    // 返回一个Optional<String>,作用同map的例子
    Optional.of(user).flatMap(u -> Optional.of(u.getName()));
    
    解决NPE

    在由Optional之前,我们会使用if…else…做防御性检查,以防止出现空指针异常。

    public String getUserName(User user) {
      if(user == null) {
        return "unknown";
      }
      return user.getName();
    }
    

    有了Optional之后,我们可以这样做了……

    public String getUserName(User user) {
      Optional<User> optional_user = Optional.ofNullable(user);
      if(!optional_user.isPresent()) {
        return "unknown";
      }
      return user.getName();
    }
    

    啊……这……

    显然这两种方法并无本质区别,依然都有防御性检查,依然都不够简洁。只不过第二种方法利用isPresent方法替换了显示的null判断。

    正确的姿势。

    public String getUserName(User user) {
      // 善用map方法
      return Optional.ofNullable(user)
                     .map(u -> u.getName())
                     .orElse("unkonwn");
    }
    

    这个例子并不能完全展示Optional的实力,再看下面的示例。

    /**
    * 获取领导姓名,不使用Optional
    * */
    public String getBossName(User user) {
      if(user != null) {
        Company company = user.getCompany();
        if(company != null) {
          Boss boss = company.getBoss();
          if(boss != null) {
            return boss.getName();
          }
        }
      }
            
      return "unkonwn";
    }
    

    优雅的姿势。

    /**
    * 获取领导姓名,使用Optional
    * */
    public String getBossName(User user) {
      return Optional.ofNullable(user)
                     .map(u -> u.getCompany())
                     .map(c -> c.getBoss())
                     .map(b -> b.getName())
                     .orElse("unkonwn");
    }
    

    善用Optional会使我们的代码无比的优雅和简洁。

    相关文章

      网友评论

          本文标题:Java8之Optional类,巧解NPE

          本文链接:https://www.haomeiwen.com/subject/lszeoktx.html