Java8新特性之03Optional
03 Optional
概述
在从数据库查询数据或者执行一些其他操作的时候,查询出来的结果可能是为空的,返回的是 null,如果不对返回值进行判断,直接对 null 进行操作,则会报空指针异常。
传统的方式是使用 if 条件判断来判断对象是否为空,并执行相应的处理逻辑:
if (authors != null) {
System.out.println(author.getName());
}
如果存在大量的非空判断,代码就会显得十分臃肿,可读性也会降低。因此 Java 8 引入了 Optional 类,用于处理可能为空的值的容器类。它提供了一种优雅的方式来处理可能存在或不存在的值,避免了空指针异常的发生。
官方文档如下:
https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
Class Optional< T>
This method supports post-processing on optional values, without the need to explicitly check for a return status. For example, the following code traverses a stream of file names, selects one that ha
以下是 Optional 类的一些重要特点和用法:
- 容器类:Optional 是一个容器类,可以包含一个非空的值或者为空。它通过 of()、ofNullable() 和 empty() 等静态方法来创建 Optional 对象。
- 避免空指针异常:Optional 提供了一种安全的方式来处理可能为空的值。通过使用 Optional,我们可以避免显式地进行空值检查,从而减少了空指针异常的风险
- 方法链操作:Optional 提供了一系列方法来对包含的值进行操作,如 map()、flatMap()、filter() 等。这些方法可以在值存在的情况下进行操作,并返回一个新的 Optional 对象。
- isPresent() 和 ifPresent():Optional 提供了 isPresent() 方法来检查值是否存在,以及 ifPresent() 方法来在值存在时执行特定的操作。
- 默认值:Optional 提供了 orElse()、orElseGet() 和 orElseThrow() 等方法来获取值或者提供默认值,以应对值为空的情况。
- 空值处理:Optional 提供了 ifPresentOrElse() 方法来在值存在或者为空时执行不同的操作。
- 使用 Optional 类可以使代码更加健壮和可读,同时提供了一种优雅的方式来处理可能为空的值。
使用
1. 创建对象
Optional 就好像是包装类,可以把我们的具体数据封装到 Optional 对象内部。然后去使用 Optional 中封装好的方法操作封装进去的数据就可以避免空指针异常。
ofNullable
一般使用 Optional 的静态方法 ofNullable(ofNullable(T value))来把数据封装成一个 Optional 对象。如果传入的参数为null,将会返回一个空的 Optional 对象。
public static void main(String[] args) {
Author author = getAuthor();
Optional<Author> optionalAuthor = Optional.ofNullable(author);
optionalAuthor.ifPresent(System.out::println);
}
这样处理感觉和使用 if 做判空好像差不多,在获取数据后都要加一行代码来处理。但是如果改造下 getAuthor 方法,让其的返回值就是封装好的 Optional 的话,我们在使用时就会方便很多。
而且在实际开发中,数据很多是从数据库获取的,MyBatis 从 3.5 版本开始也已经支持 Optional 了,可以直接把 Dao 层方法的返回值类型定义成 Optional 类型,封装的过程也不需要自己操作,MyBatis 会自己把数据封装成 Optional 对象返回。
of
of(T value):of 方法只能处理非空的对象。如果确定一个对象不是空的,则可以使用 of 方法来把数据封装成 Optional 对象。
empty
empty():返回一个空的 Optional 对象。如果明确知道某个对象为 null,那么可以直接使用 empty() 封装。
ofNullable() 本质也是调用 of() 和 empty() 实现的:
public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); }
2. 安全消费值
ifPresent(Consumer<? super T> consumer):如果 Optional 对象中的对象不为 null,那么就会调用 consumer 中定义的逻辑处理,如果为 null,则不执行任何操作。
获取到一个 Optional 之后,可以使用其 ifPresent() 方法来消费其中的值。这个方法会判断其内封装的数据是否为空,不为空时才会执行具体的消费代码,这样使用起来就更加安全了。
例如,以下写法就避免了空指针异常。
public static void main(String[] args) {
Author author = getAuthor();
Optional<Author> optionalAuthor = Optional.ofNullable(author);
optionalAuthor.ifPresent(System.out::println);
}
3. 获取值
get():如果 Optional 中的值不为 null,则返回该对象,如果为空,则会抛 NoSuchElementException 异常。
如果想获取值自己进行处理,可以使用 get() 方法获取,但是因为空的 Optional 调用 get() 会抛异常,因此还需要使用 isPresent() 方法判断 Optional 中值是否存在,如果存在返回 true,否则返回 false。
public static void main(String[] args) {
Author author = getAuthor();
Optional<Author> optionalAuthor = Optional.of(author);
if (optionalAuthor.isPresent()) {
Author author2 = optionalAuthor.get();
System.out.println(author2);
}
}
但是这种方式和直接对对象进行判空差不多,因此,建议直接使用 ifPresent 方法。
4. 安全获取值
直接使用 get() 方法获取值还是可能存在异常问题,因此,如果期望安全地获取值,可以使用 Optional 的下列方法。
orElseGet
orElseGet(Supplier<? extends T> other):如果存在则返回值,否则返回设置好的默认值。
例如:
public static void main(String[] args) {
Author author = getAuthor();
Optional<Author> optionalAuthor = Optional.of(author);
Author author2 = optionalAuthor.orElseGet(new Supplier<Author>() {
@Override
public Author get() {
return new Author();
}
});
}
改用 Lambda 表达式
public static void main(String[] args) {
Author author = getAuthor();
Optional<Author> optionalAuthor = Optional.of(author);
Author author2 = optionalAuthor.orElseGet(() -> new Author());
}
orElseThrow
orElseThrow(Supplier<? extends X> exceptionSupplier):如果存在则返回值,否则抛出 Supplier 指定的异常。
该方法在 Spring 框架中用的比较多,可以用 Spring 来做统一的异常捕获。
public static void main(String[] args) throws Throwable {
Author author = getAuthor();
Optional<Author> optionalAuthor = Optional.of(author);
Author author2 = optionalAuthor.orElseThrow(new Supplier<Throwable>() {
@Override
public Throwable get() {
return new RuntimeException();
}
});
}
改为 Lambda 表达式:
public static void main(String[] args) throws Throwable {
Author author = getAuthor();
Optional<Author> optionalAuthor = Optional.of(author);
Author author2 = optionalAuthor.orElseThrow(() -> new RuntimeException());
}
5. 过滤
filter(Predicate<? super T> predicate):如果存在值并且值满足设定的条件,返回满足条件的元素组成的 Optional,否则返回空的 Optional。
public static void main(String[] args) throws Throwable {
Author author = getAuthor();
Optional<Author> optionalAuthor = Optional.ofNullable(author);
optionalAuthor.filter(author1 -> author1.getAge() > 18).ifPresent(author1 -> System.out.println(author1.getName()));
}
6. 判断
isPresent():对Optional对象中是否存在值进行判断,如果存在返回true,否则返回false。
一般都是直接使用 ifPresent(),单独使用 isPresent() 和使用 if(xx != null) 差不多。
7. 数据转换
map(Function<? super T,? extends U> mapper):与 Stream 中的 map() 方法类似,用于数据转换或计算。
例如获取作家书籍的集合:
public static void main(String[] args) throws Throwable {
Optional<Author> optionalAuthor = Optional.ofNullable(getAuthor());
// 使用map将 Optional<Author> 转换为 Optional<List<Book>>
Optional<List<Book>> optionalBooks = optionalAuthor.map(new Function<Author, List<Book>>() {
@Override
public List<Book> apply(Author author) {
return author.getBooks();
}
});
// 如果Optional<List<Book>> 不为空,取出数据进行消费。
optionalBooks.ifPresent(new Consumer<List<Book>>() {
@Override
public void accept(List<Book> books) {
// 遍历books
books.forEach(new Consumer<Book>() {
@Override
public void accept(Book book) {
System.out.println(book.getName());
}
});
}
});
}
改为 Lambda 表达式:
public static void main(String[] args) throws Throwable {
Optional<Author> optionalAuthor = Optional.ofNullable(getAuthor());
Optional<List<Book>> optionalBooks = optionalAuthor.map(author -> author.getBooks());
optionalBooks.ifPresent(books -> books.forEach(book -> System.out.println(book.getName())));
}