Java8之熟透Optional

1、使用Optional引言

1.一、代码问题引出

在写程序的时候通常都遇到过 NullPointerException,因此常常会对程序进行非空的判断:java

User user = getUserById(id);
if (user != null) {
    String username = user.getUsername();
    System.out.println("Username is: " + username); // 使用 username
}
复制代码

为了解决这种尴尬的处境,JDK 终于在 Java8 的时候加入了 Optional 类,查看 Optional 的 javadoc 介绍:数据库

A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value. 复制代码

这是一个能够包含或者不包含非 null 值的容器。若是值存在则 isPresent()方法会返回 true,调用 get() 方法会返回该对象。markdown

1.二、解决进阶

咱们假设 getUserById 已是个客观存在的不能改变的方法,那么利用 isPresentget 两个方法,咱们如今能写出下面的代码:app

Optional<User> user = Optional.ofNullable(getUserById(id));
if (user.isPresent()) {
    String username = user.get().getUsername();
    System.out.println("Username is: " + username); // 使用 username
}
复制代码

好像看着代码是优美了点,可是事实上这与以前判断 null 值的代码没有本质的区别,反而用 Optional 去封装 value,增长了代码量。因此咱们来看看 Optional 还提供了哪些方法,让咱们更好的(以正确的姿式)使用 Optionalless

2、Optional三个静态构造方法

1)概述:函数

JDK 提供三个静态方法来构造一个 Optionalui

  1. Optional.of(T value)this

    public static <T> Optional<T> of(T value) {
            return new Optional<>(value);
        }
    复制代码

    该方法经过一个非 nullvalue 来构造一个 Optional,返回的 Optional 包含了 value 这个值。对于该方法,传入的参数必定不能为 null,不然便会抛出 NullPointerExceptionspa

  2. Optional.ofNullable(T value)code

    public static <T> Optional<T> ofNullable(T value) {
            return value == null ? empty() : of(value);
        }
    复制代码

    该方法和 of 方法的区别在于,传入的参数能够为 null —— 可是前面 javadoc 不是说 Optional 只能包含非 null 值吗?咱们能够看看 ofNullable 方法的源码。

    原来该方法会判断传入的参数是否为 null,若是为 null 的话,返回的就是 Optional.empty()

  3. Optional.empty()

    public static<T> Optional<T> empty() {
            @SuppressWarnings("unchecked")
            Optional<T> t = (Optional<T>) EMPTY;
            return t;
        }
    复制代码

    该方法用来构造一个空的 Optional,即该 Optional 中不包含值 —— 其实底层实现仍是 若是 Optional 中的 value 为 null 则该 Optional 为不包含值的状态,而后在 API 层面将 Optional 表现的不能包含 null值,使得 Optional 只存在 包含值不包含值 两种状态。

2)分析:

前面 javadoc 也有提到,OptionalisPresent() 方法用来判断是否包含值,get() 用来获取 Optional 包含的值 —— 值得注意的是,若是值不存在,即在一个Optional.empty 上调用 get() 方法的话,将会抛出 NoSuchElementException异常

3)总结:

1)Optional.of(obj): 它要求传入的 obj 不能是 null 值的, 不然还没开始进入角色就倒在了 NullPointerException 异常上了. 2)Optional.ofNullable(obj): 它以一种智能的, 宽容的方式来构造一个 Optional 实例. 来者不拒, 传 null 进到就获得 Optional.empty(), 非 null 就调用 Optional.of(obj). 那是否是咱们只要用 Optional.ofNullable(obj) 一劳永逸, 以不变应二变的方式来构造 Optional 实例就好了呢? 那也未必, 不然 Optional.of(obj) 何须如此暴露呢, 私有则可。

3、Optional经常使用方法详解

3.一、Optional经常使用方法概述

  1. Optional.of(T t)

    将指定值用 Optional 封装以后返回,若是该值为 null,则抛出一个 NullPointerException 异常。

  2. Optional.empty()

    建立一个空的 Optional 实例。

  3. Optional.ofNullable(T t)

    将指定值用 Optional 封装以后返回,若是该值为 null,则返回一个空的 Optional 对象。

  4. isPresent

    若是值存在返回true,不然返回false

  5. ifPresent

    若是Optional实例有值则为其调用consumer ,不然不作处理。 要理解ifPresent方法,首先须要了解Consumer类。简答地说,Consumer类包含一个抽象方法。该抽象方法对传入的值进行处理,但没有返回值。 Java8支持不用接口直接经过lambda表达式传入参数。 若是Optional实例有值,调用ifPresent()能够接受接口段或lambda表达式。

  6. Optional.get()

    若是该值存在,将该值用 Optional 封装返回,不然抛出一个 NoSuchElementException 异常。

  7. orElse(T t)

    若是调用对象包含值,返回该值,不然返回t。

  8. orElseGet(Supplier s)

    若是调用对象包含值,返回该值,不然返回 s 获取的值。

  9. orElseThrow()

    它会在对象为空的时候抛出异常。

  10. map(Function f)

    若是值存在,就对该值执行提供的 mapping 函数调用。

  11. flatMap(Function mapper)

    若是值存在,就对该值执行提供的mapping 函数调用,返回一个 Optional 类型的值,不然就返回一个空的 Optional 对象。

3.二、Optional经常使用方法详解

3.2.一、ifPresent
public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }
复制代码

若是 Optional 中有值,则对该值调用 consumer.accept,不然什么也不作。 因此对于引言上的例子,咱们能够修改成:

Optional<User> user = Optional.ofNullable(getUserById(id));
user.ifPresent(u -> System.out.println("Username is: " + u.getUsername()));
复制代码
3.2.二、orElse
public T orElse(T other) {
        return value != null ? value : other;
    }
复制代码

若是 Optional 中有值则将其返回,不然返回 orElse 方法传入的参数。

User user = Optional
        .ofNullable(getUserById(id))
        .orElse(new User(0, "Unknown"));
        
System.out.println("Username is: " + user.getUsername());
复制代码
3.2.三、orElseGet
public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }
复制代码

orElseGetorElse 方法的区别在于,orElseGet 方法传入的参数为一个 Supplier接口的实现 —— 当 Optional 中有值的时候,返回值;当 Optional 中没有值的时候,返回从该 Supplier 得到的值。

User user = Optional
        .ofNullable(getUserById(id))
        .orElseGet(() -> new User(0, "Unknown"));
        
System.out.println("Username is: " + user.getUsername());
复制代码
3.2.四、orElseThrow
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }
复制代码

orElseThroworElse 方法的区别在于,orElseThrow 方法当 Optional 中有值的时候,返回值;没有值的时候会抛出异常,抛出的异常由传入的 exceptionSupplier 提供。

举例说明:

​ 在 SpringMVC 的控制器中,咱们能够配置统一处理各类异常。查询某个实体时,若是数据库中有对应的记录便返回该记录,不然就能够抛出 EntityNotFoundException ,处理 EntityNotFoundException 的方法中咱们就给客户端返回Http 状态码 404 和异常对应的信息 —— orElseThrow 完美的适用于这种场景。

@RequestMapping("/{id}")
public User getUser(@PathVariable Integer id) {
    Optional<User> user = userService.getUserById(id);
    return user.orElseThrow(() -> new EntityNotFoundException("id 为 " + id + " 的用户不存在"));
}

@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<String> handleException(EntityNotFoundException ex) {
    return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
复制代码
3.2.五、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));
        }
    }
复制代码

若是当前 OptionalOptional.empty,则依旧返回 Optional.empty;不然返回一个新的 Optional,该 Optional 包含的是:函数 mapper 在以 value 做为输入时的输出值。

String username = Optional.ofNullable(getUserById(id))
                        .map(user -> user.getUsername())
                        .orElse("Unknown")
                        .ifPresent(name -> System.out.println("Username is: " + name));
复制代码

并且咱们能够屡次使用 map 操做:

Optional<String> username = Optional.ofNullable(getUserById(id))
                                .map(user -> user.getUsername())
                                .map(name -> name.toLowerCase())
                                .map(name -> name.replace('_', ' '))
                                .orElse("Unknown")
                                .ifPresent(name -> System.out.println("Username is: " + name));
复制代码
3.2.六、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));
        }
    }
复制代码

flatMap 方法与 map 方法的区别在于,map 方法参数中的函数 mapper 输出的是值,而后 map 方法会使用 Optional.ofNullable 将其包装为 Optional;而 flatMap 要求参数中的函数 mapper 输出的就是 Optional

Optional<String> username = Optional.ofNullable(getUserById(id))
                                .flatMap(user -> Optional.of(user.getUsername()))
                                .flatMap(name -> Optional.of(name.toLowerCase()))
                                .orElse("Unknown")
                                .ifPresent(name -> System.out.println("Username is: " + name));
复制代码
3.2.七、filter
public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }
复制代码

filter 方法接受一个 Predicate 来对 Optional 中包含的值进行过滤,若是包含的值知足条件,那么仍是返回这个 Optional;不然返回 Optional.empty

Optional<String> username = Optional.ofNullable(getUserById(id))
                                .filter(user -> user.getId() < 10)
                                .map(user -> user.getUsername());
                                .orElse("Unknown")
                                .ifPresent(name -> System.out.println("Username is: " + name));
复制代码

4、Optional使用示例

4.一、使用展现一

当 user.isPresent() 为真, 得到它关联的 orders的映射集合, 为假则返回一个空集合时, 咱们用上面的 orElse, orElseGet 方法都乏力时, 那本来就是 map 函数的责任, 咱们能够这样一行:

return user.map(u -> u.getOrders()).orElse(Collections.emptyList())
 
//上面避免了咱们相似 Java 8 以前的作法
if(user.isPresent()) {
  return user.get().getOrders();
} else {
  return Collections.emptyList();
}
复制代码

map 是可能无限级联的, 好比再深一层, 得到用户名的大写形式:

return user.map(u -> u.getUsername())
           .map(name -> name.toUpperCase())
           .orElse(null);
复制代码

之前的作法:

User user = .....
if(user != null) {
  String name = user.getUsername();
  if(name != null) {
    return name.toUpperCase();
  } else {
    return null;
  }
} else {
  return null;
}
复制代码

filter() :若是有值而且知足条件返回包含该值的Optional,不然返回空Optional。

Optional<String> longName = name.filter((value) -> value.length() > 6);  
System.out.println(longName.orElse("The name is less than 6 characters")); 
复制代码
相关文章
相关标签/搜索