在写程序的时候通常都遇到过 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
咱们假设 getUserById
已是个客观存在的不能改变的方法,那么利用 isPresent
和 get
两个方法,咱们如今能写出下面的代码: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
还提供了哪些方法,让咱们更好的(以正确的姿式)使用 Optional
。less
1)概述:函数
JDK 提供三个静态方法来构造一个 Optional
:ui
Optional.of(T value)this
public static <T> Optional<T> of(T value) { return new Optional<>(value); } 复制代码
该方法经过一个非 null
的 value 来构造一个 Optional
,返回的 Optional
包含了 value 这个值。对于该方法,传入的参数必定不能为 null
,不然便会抛出 NullPointerException
。spa
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()
。
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 也有提到,Optional
的 isPresent()
方法用来判断是否包含值,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) 何须如此暴露呢, 私有则可。
Optional.of(T t)
将指定值用 Optional 封装以后返回,若是该值为 null,则抛出一个 NullPointerException 异常。
Optional.empty()
建立一个空的 Optional 实例。
Optional.ofNullable(T t)
将指定值用 Optional 封装以后返回,若是该值为 null,则返回一个空的 Optional 对象。
isPresent
若是值存在返回true,不然返回false
ifPresent
若是Optional实例有值则为其调用consumer ,不然不作处理。 要理解ifPresent方法,首先须要了解Consumer类。简答地说,Consumer类包含一个抽象方法。该抽象方法对传入的值进行处理,但没有返回值。 Java8支持不用接口直接经过lambda表达式传入参数。 若是Optional实例有值,调用ifPresent()能够接受接口段或lambda表达式。
Optional.get()
若是该值存在,将该值用 Optional 封装返回,不然抛出一个 NoSuchElementException 异常。
orElse(T t)
若是调用对象包含值,返回该值,不然返回t。
orElseGet(Supplier s)
若是调用对象包含值,返回该值,不然返回 s 获取的值。
orElseThrow()
它会在对象为空的时候抛出异常。
map(Function f)
若是值存在,就对该值执行提供的 mapping 函数调用。
flatMap(Function mapper)
若是值存在,就对该值执行提供的mapping 函数调用,返回一个 Optional 类型的值,不然就返回一个空的 Optional 对象。
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())); 复制代码
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()); 复制代码
public T orElseGet(Supplier<? extends T> other) { return value != null ? value : other.get(); } 复制代码
orElseGet
与 orElse
方法的区别在于,orElseGet
方法传入的参数为一个 Supplier
接口的实现 —— 当 Optional
中有值的时候,返回值;当 Optional
中没有值的时候,返回从该 Supplier
得到的值。
User user = Optional .ofNullable(getUserById(id)) .orElseGet(() -> new User(0, "Unknown")); System.out.println("Username is: " + user.getUsername()); 复制代码
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { if (value != null) { return value; } else { throw exceptionSupplier.get(); } } 复制代码
orElseThrow
与 orElse
方法的区别在于,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); } 复制代码
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
为 Optional.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)); 复制代码
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)); 复制代码
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)); 复制代码
当 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")); 复制代码