目录
  1. 1. Optional类
    1. 1.1. 构造Optional
    2. 1.2. Optional的方法
      1. 1.2.1. isPresent()
      2. 1.2.2. get()
      3. 1.2.3. orElse()
      4. 1.2.4. orElseGet
      5. 1.2.5. orElseThrow
        1. 1.2.5.1. orElseThrow示例
    3. 1.3. 其他方法
      1. 1.3.1. map
      2. 1.3.2. flatmap
      3. 1.3.3. filter
    4. 1.4. JDK9的增强
Optional类

Optional类

我们在写Java代码的时候,通常会遇到==NullPointerException==,为了不抛出这个异常,我们一般会写如下代码:

1
2
3
4
5
User user = getUserById(id);
if (user != null) {
String username = user.getUsername();
System.out.println("Username is: " + username); // 使用 username
}

但是很多时候,我们可能会忘记写==if(user!=null)==
  如果在开发阶段就发现那还好,但是如果在开发阶段没有测试到问题,等到上线却出了==NullPointerException==画面太美,我不敢继续想下去
为了解决这种尴尬的处境,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()方法会返回该对象

构造Optional

JDK提供三个静态方法来构造一个==Optional==:
1.==Optional.of(T value)==:该方法通过一个非null的value来构造一个 Optional,返回的Optional包含了value这个值.对于该方法,传入的参数一定不能为null,否则便会抛出NullPointerException
具体源码如下:

1
2
3
4
5
6
7
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}

private Optional(T value) {
this.value = Objects.requireNonNull(value);
}

2.==Optional.ofNullable(T value)==:该方法和of方法的区别在于–>传入的参数可以为 null
  但是前面javadoc不是说Optional只能包含非null值吗?我们可以看看ofNullable方法的源码

1
2
3
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}

  原来该方法会判断传入的参数是否为null,如果为null的话,返回的就是Optional.empty()
3.==Optional.empty()==:该方法用来构造一个空的Optional,即该Optional中不包含值
  其实底层实现:如果Optional中的value 为null则该Optional为不包含值的状态,然后在API层面将Optional表现的不能包含null值,使得Optional只存在包含值不包含值两种状态
具体源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}

private static final Optional<?> EMPTY = new Optional<>();

private Optional() {
this.value = null;
}

private final T value;

Optional的方法

isPresent()

Optional的isPresent()方法用来判断是否包含值

1
2
3
4
5
6
7
8
public boolean isPresent() {
return value != null;
}

public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}

  如果Optional中有值,则对该值调用consumer.accept,否则什么也不做
对于前面的getUserById例子,可以修改为:

1
2
Optional<User> user = Optional.ofNullable(getUserById(id));
user.ifPresent(u -> System.out.println("Username is: " + u.getUsername()));

get()

get()用来获取Optional包含的值
注意:

如果值不存在,即在一个Optional.empty上调用get()方法的话,将会抛出NoSuchElementException异常

orElse()

1
2
3
public T orElse(T other) {
return value != null ? value : other;
}

如果Optional中有值则将其返回,否则返回orElse方法传入的参数

1
2
3
4
5
User user = Optional
.ofNullable(getUserById(id))
.orElse(new User(0, "Unknown"));

System.out.println("Username is: " + user.getUsername());

orElseGet

1
2
3
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}

orElseGet与orElse方法的区别在于:orElseGet方法传入的参数为一个Supplier接口的实现

当Optional中有值的时候,返回值;当Optional中没有值的时候,返回从该Supplier获得的值

orElseThrow

1
2
3
4
5
6
7
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提供

1
2
3
User user = Optional
.ofNullable(getUserById(id))
.orElseThrow(() -> new EntityNotFoundException("id 为 " + id + " 的用户没有找到"));

orElseThrow示例

在SpringMVC的控制器中,我们可以配置统一处理各种异常.查询某个实体时,如果数据库中有对应的记录便返回该记录,否则就可以抛出EntityNotFoundException,处理EntityNotFoundException的方法中我们就给客户端返回Http状态码 404和异常对应的信息——orElseThrow完美的适用于这种场景

1
2
3
4
5
6
7
8
9
10
@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);
}

其他方法

map

1
2
3
4
5
6
7
8
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作为输入时的输出值,例子如下:

1
2
3
4
5
Optional<String> username = Optional
.ofNullable(getUserById(id))
.map(user -> user.getUsername());

System.out.println("Username is: " + username.orElse("Unknown"));

多次map操作如下:

1
2
3
4
5
6
7
Optional<String> username = Optional
.ofNullable(getUserById(id))
.map(user -> user.getUsername())
.map(name -> name.toLowerCase())
.map(name -> name.replace('_', ' '));

System.out.println("Username is: " + username.orElse("Unknown"));

flatmap

1
2
3
4
5
6
7
8
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
具体示例如下:

1
2
3
4
5
6
Optional<String> username = Optional
.ofNullable(getUserById(id))
.flatMap(user -> Optional.of(user.getUsername()))
.flatMap(name -> Optional.of(name.toLowerCase()));

System.out.println("Username is: " + username.orElse("Unknown"));

filter

1
2
3
4
5
6
7
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

1
2
3
4
5
6
Optional<String> username = Optional
.ofNullable(getUserById(id))
.filter(user -> user.getId() < 10)
.map(user -> user.getUsername());

System.out.println("Username is: " + username.orElse("Unknown"));

JDK9的增强

1.public Optional or(Supplier<? extends Optional<? extends T>> supplier) or 方法的作用是:如果一个Optional包含值,则返回自己;否则返回由参数supplier获得的Optional
2.public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) ifPresentOrElse方法的用途是:如果一个Optional包含值,则对其包含的值调用函数action,即action.accept(value),这与ifPresent一致;与ifPresent方法的区别在于:ifPresentOrElse还有第二个参数emptyAction——如果Optional不包含值,那么ifPresentOrElse便会调用emptyAction,即emptyAction.run()
3.public Stream stream() stream方法的作用就是将Optional 转为一个 Stream,如果该Optional中包含值,那么就返回包含这个值的Stream;否则返回一个空的Stream(Stream.empty())
具体示例:
JDK8的写法:

1
2
3
4
5
6
7
8
// 此处 getUserById 返回的是 Optional<User>
public List<User> getUsers(Collection<Integer> userIds) {
return userIds.stream()
.map(this::getUserById) // 获得 Stream<Optional<User>>
.filter(Optional::isPresent)// 去掉不包含值的 Optional
.map(Optional::get)
.collect(Collectors.toList());
}

JDK9的写法:

1
2
3
4
5
6
public List<User> getUsers(Collection<Integer> userIds) {
return userIds.stream()
.map(this::getUserById) // 获得 Stream<Optional<User>>
.flatMap(Optional::stream) // Stream 的 flatMap 方法将多个流合成一个流
.collect(Collectors.toList());
}
文章作者: Eric Liang
文章链接: https://ericql.github.io/2019/11/12/01-Java%E5%9F%BA%E7%A1%80%E7%AF%87/04-Java8%E6%96%B0%E7%89%B9%E6%80%A7/Optional%E7%B1%BB/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Eric Liang
打赏
  • 微信
  • 支付宝