您的位置:首页 > 编程语言 > Java开发

Java 8 Optional类深度解析

2016-08-16 22:15 465 查看
   思考: 调用一个方法得到了返回值却不能直接将返回值作为参数去调用别的方法。

原来解决方案: 我们首先要判断这个返回值是否为null,只有在非空的前提下才能将其作为其他方法的参数。这正是一些类似Guava的外部API试图解决的问题。

        一些JVM编程语言比如Scala、Ceylon等已经将对在核心API中解决了这个问题。

新版本的Java,比如Java 8引入了一个新的Optional类。Optional类的Javadoc描述如下:


这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。


本文会逐个探讨Optional类包含的方法,并通过一两个示例展示如何使用。

of


为非null的值创建一个Optional。


  of方法通过工厂方法创建Optional类。需要注意的是,创建对象时传入的参数不能为null。如果传入参数为null,则抛出NullPointerException 。

ofNullable


为指定的值创建一个Optional,如果指定的值为null,则返回一个空的Optional。


ofNullable与of方法相似,唯一的区别是可以接受参数为null的情况。示例如下:

isPresent

非常容易理解


如果值存在返回true,否则返回false。


类似下面的代码:

get


如果Optional有值则将其返回,否则抛出NoSuchElementException。


上面的示例中,get方法用来得到Optional实例中的值。下面我们看一个抛出NoSuchElementException的例子:

ifPresent  


如果Optional实例有值则为其调用consumer,否则不做处理


要理解ifPresent方法,首先需要了解Consumer类。简答地说,Consumer类包含一个抽象方法。该抽象方法对传入的值进行处理,但没有返回值。

Java8支持不用接口直接通过lambda表达式传入参数。

如果Optional实例有值,调用ifPresent()可以接受接口段或lambda表达式。类似下面的代码:

orElse


如果有值则将其返回,否则返回指定的其它值。


如果Optional实例有值则将其返回,否则返回orElse方法传入的参数。示例如下:

orElseGet

orElseGet与orElse方法类似,区别在于得到的默认值。orElse方法将传入的字符串作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值。示例如下:

orElseThrow


如果有值则将其返回,否则抛出supplier接口创建的异常。


在orElseGet方法中,我们传入一个Supplier接口。然而,在orElseThrow中我们可以传入一个lambda表达式或方法,如果值不存在来抛出异常。示例如下:

ValueAbsentException定义如下:

map

map方法文档说明如下:


如果有值,则对其执行调用mapping函数得到返回值。如果返回值不为null,则创建包含mapping返回值的Optional作为map方法返回值,否则返回空Optional。


map方法用来对Optional实例的值执行一系列操作。通过一组实现了Function接口的lambda表达式传入操作。map方法示例如下:

flatMap


如果有值,为其执行mapping函数返回Optional类型返回值,否则返回空Optional。flatMap与map(Funtion)方法类似,区别在于flatMap中的mapper返回值必须是Optional。调用结束时,flatMap不会对结果用Optional封装。


flatMap方法与map方法类似,区别在于mapping函数的返回值不同。map方法的mapping函数返回值可以是任何类型T,而flatMap方法的mapping函数必须是Optional。

参照map函数,使用flatMap重写的示例如下:

filter

filter个方法通过传入限定条件对Optional实例的值进行过滤。文档描述如下:


如果有值并且满足断言条件返回包含该值的Optional,否则返回空Optional。


读到这里,可能你已经知道如何为filter方法传入一段代码。是的,这里可以传入一个lambda表达式。对于filter函数我们应该传入实现了Predicate接口的lambda表达式。如果你不熟悉Predicate接口,可以参考这篇文章

现在我来看看filter的各种用法,下面的示例介绍了满足限定条件和不满足两种情况:

以上,我们介绍了Optional类的各个方法。下面通过一个完整的示例对用法集中展示:

上述代码输出如下:

实例展示

假如有一个Person 对象,它有一个Address 属性,而Address属性还嵌套一个validFrom的日期。所有的值都可能是null。
好了,现在我们来判断一下Person 的这些属性是否是Valid的。

1
2
3
4
5
6
7
8
9
10

private boolean validAddress(NullPerson person) {
if (person != null) {
if (person.getAddress() != null) {
final Instant validFrom = person.getAddress().getValidFrom();
return validFrom != null && validFrom.isBefore(now());
} else
return false;
} else
return false;
}

或者也可以这么写:

1
2
3
4

return person != null &&
person.getAddress() != null &&
person.getAddress().getValidFrom() != null &&
person.getAddress().getValidFrom().isBefore(now());

总之 都不是很好看。

但如果这些属性都是Optional,那么看起来会稍微舒服一点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

class Person {

private final Optional<Address> address;

public Optional<Address> getAddress() {
return address;
}

//...
}

class Address {
private final Optional<Instant> validFrom;

public Optional<Instant> getValidFrom() {
return validFrom;
}

//...
}

以下代码就实现了一样的判断:

1
2
3
4
5

return person.
flatMap(Person::getAddress).
flatMap(Address::getValidFrom).
filter(x -> x.before(now())).
isPresent();

使用Optional以后,NullPointerException 就从此消失了。

将一个Optional转为List或者Set

Optional是一个集合,虽然里面只有0或者1个元素,但它一样是一个集合。如果要转为List或者Set,一般的写法可以是:

1
2
3
4
5

public static <T> List<T> toList(Optional<T> option) {
return option.
map(Collections::singletonList).
orElse(Collections.emptyList());
}

或者更传统的写法:

1
2
3
4
5
6

public static <T> List<T> toList(Optional<T> option) {
if (option.isPresent())
return Collections.singletonList(option.get());
else
return Collections.emptyList();
}

但是在java8里,其实只需要这么写:

1
2
3
4
5

import static java.util.stream.Collectors.*;
//转为List
List<String> list = collect(opt, toList());
//转为Set
Set<String> set = collect(opt, toSet());

本文代码内容来自于:http://www.nurkiewicz.com/2013/08/optional-in-java-8-cheat-sheet.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: