Optional解决空指针
NPE问题
NPE问题就是,我们在开发中经常碰到的NullPointerException。
下面这种写法是比较丑陋的,为了变得优雅。
JAVA8提供了Optional类来优化这种写法
1
2
3
4
5
6
7
8user.getAddress().getProvince();
// 在user为null时,是有可能报NullPointerException异常的,于是采用下面的写法
if(user!=null){
Address address = user.getAddress();
if(address!=null){
String province = address.getProvince();
}
}
Optional类型
Optional<T>
对象是一种包装器对象,要么包装了类型T
的对象,要么没有包装任何对象。- Optional的本质,就是内部储存了一个真实的值,在构造的时候,就直接判断其值是否为空
Optional<T>
类型被当作一种更安全的方式,用来替代类型T的引用,这种引用要么引用某个对象,要么为null。Optional 类的引入很好的解决空指针异常。
创建Optional值
API如下
API 修饰符 NullPointerException optional(T value) private 构造函数,不能由外部调用的 empty() public 产生一个空的Optional,null of(T value) public 产生一个具有给定值的Optional 会 ofNullable(T value) public 产生一个具有给定值的Optional 不会 源码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25public final class Optional<T> {
//省略....
private static final Optional<?> EMPTY = new Optional<>();
private Optional() {
this.value = null;
}
// 产生一个具有给定值的Optional。如果value为null,那么会抛出NullPointerException异常
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
// 产生一个具有给定值的Optional。如果value为null,那么会产生一个空Optional
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
// 产生一个空的Optional
public static<T> Optional<T> empty() {
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
}例如:
1
2
3public static Optional<Double> inverse(Double x){
return x == 0 ? Optional.empty() : Optional.of(1 / x);
}ofNullable
方法被用来作为可能出现的null值和可选值之间的桥梁。- 该方法会在obj不为null的情况下返回
Optional.of(obj)
,否则返回Optional.empty()
。
- 该方法会在obj不为null的情况下返回
获取Optional的值
get
方法会在Optional值存在的情况下获得其中包装的元素,或者在不存在的情况下抛出一个NoSuchElementEception
对象。1
2
3
4
5
6
7
8
9
10public final class Optional<T> {
//产生这个Optional的值,或者在该Optional为空时,抛出一个NoSuchElementException异常
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
}如果没有正确地设置Optional值,那么相比以前得到“null”的方式,直接抛出异常显然不是很好。
1
2
3
4
5
6Optional<T> optionalValue = ...;
optionalValue.get().someMethod();
//并不比下面的方式更安全
T value = ...;
value.someMehtod();结合判断值使用,代码结构依然丑陋
1
2
3
4
5
6
7
8
9if(optionalValue.isPresent()){
optionalValue.get().someMethod();
}
//并不比下面的方式更容易处理
if(value != null){
value.someMethod();
}推荐写法
1
2
3
4
5
6
7
8if(user!=null){
dosomething(user);
}
Optional.ofNullable(user)
.ifPresent(u->{
dosomething(u);
});
使用Optional值
API如下
API orElse(T other) value值为null时,给予一个默认值 值不为null时,orElse函数依然会执行 orElseGet(Supplier<? extends T> other) value值为null时,给予一个默认值 值不为null时,orElseGet函数并不会执行 orElseThrow(Supplier<? extends X> exceptionSupplier) value值为null时,抛一个异常 map(Function<? super T, ? extends U> mapper) 转换值 flatMap(Function<? super T, Optional> mapper) 转换值 源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42public final class Optional<T> {
//产生这个Optional的值,或者在该Optional为空时,产生other
public T orElse(T other) {
return value != null ? value : other;
}
//产生这个Optional的值,或者在该Optional为空时,产生调用other的结果
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
//产生将该Optional的值传递给mapper后的结果,只要这个Optional不为空且结果不为null,
//否则产生一个空Optional
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
//产生将该Optional的值传递给mapper后的结果,只要这个Optional不为空且结果不为null,否则产生空Optional
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));
}
}
//产生将mapper应用于当前Optional值所产生的结果,或者在当前Optional为空时,返回一个空Optional
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));
}
}
}有效使用Optional的关键是要使用这样的方法:
- 它在值不存在的情况下会产生一个可替代物,而只有在值存在的情况下才会使用这个值。
1
2
3
4
5
6
7
8//有值使产生string,无值时产生空字符串""
String result = optionalString.orElse("");
//值不存在的时候调用这个lambda表达式,计算默认值
String result = optionalString.orElseGet(() -> Local.getDefault().getDisplayName());
//在没有任何值时抛出异常
String result = optionalString.orElseThrow(IllegalStateException::new);使用可选值的策略是只有其存在的情况下才消费该值
ifPresent
方法会接受一个函数。如果该可选值存在,那么它会被传递给该函数。否则,不发生任何事情。1
2
3
4//如果值存在时添加到某个集合中
optionaValue.ifPresent(v -> results.add(v));
//或者这样用,lambda表达式的方法引用
optionaValue.ifPresent(results::add);当调用
ifPresent
时,从该函数不会返回任何值。如果想要处理函数的结果,应该使用map1
2Optional<Boolean> added = optionalValue.map(results::add);
//若值存在,Optional为处理结果true或者false,若不存在,则为空Optional
map&flatMap具体用法
这两个函数,在函数体上没什么区别。唯一区别的就是入参,map函数所接受的入参类型为
Function<? super T, ? extends U>
,而flapMap的入参类型为Function<? super T, Optional<U>>
。- flapMap 能处理值是Optional类型的数据
如果User结构是下面这样的
1
2
3
4
5
6
7
8
9public class User {
private String name;
public String getName() {
return name;
}
}
// 这时候取name的写法如下所示
String name = Optional.ofNullable(user).map(u-> u.getName()).get();对于flatMap而言:如果User结构是下面这样的
- 如果想获取user的城市名称,普通类型是可以直接 user.getAddress().getCity()的方式获取
- 使用Optional后,user.getAddress()的类型为Optional
<Address>
,而不是Address。 - 为了解决这个问题,我们可以调用flatMap方法获取到Address
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public class User {
private String name;
private Optional<Address> address;
public Optional<String> getName() {
return Optional.ofNullable(name);
}
}
public class Address {
private String city;
public Optional<String> getCity() {
return Optional.ofNullable(city);
}
}
// 这时候取city值的写法如下所示
String city = user.getAddress().flatMap(u -> u.getCity()).get();
city = Optional.ofNullable(user).flatMap(User::getAddress).get().getCity();
判断Optional值
API
API isPresent() 判断value值是否不为空 ifPresent(Consumer<? super T> consumer) 在value值不为空时,做一些操作 源码
1
2
3
4
5
6
7
8
9
10
11
12public final class Optional<T> {
// 判断value值是否不为空
public boolean isPresent() {
return value != null;
}
// 如果该Optional不为空,那么就将它的值传递给consumer
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
}使用
1
2
3Optional.ofNullable(user).ifPresent(u->{
// TODO: do something
});
过滤Optional值
filter 方法接受一个
Predicate
来对Optional
中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个 Optional;否则返回Optional.empty
。1
2
3
4
5
6
7
8
9
10public final class Optional<T> {
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!this.isPresent()) {
return this;
} else {
return predicate.test(this.value) ? this : empty();
}
}
}使用
1
2
3// user的name的长度是小于6的,则返回。如果是大于6的,则返回一个EMPTY对象。
Optional<User> user1 = Optional.ofNullable(user)
.filter(u -> u.getName().length()<6);
实战使用
从对象取值
以前写法
1
2
3
4
5
6
7
8
9
10
11public String getCity(User user) throws Exception{
if(user!=null){
if(user.getAddress()!=null){
Address address = user.getAddress();
if(address.getCity()!=null){
return address.getCity();
}
}
}
throw new Excpetion("取值错误");
}JAVA8写法
1
2
3
4
5
6public String getCity(User user) throws Exception{
return Optional.ofNullable(user)
.map(u-> u.getAddress())
.map(a->a.getCity())
.orElseThrow(()->new Exception("取指错误"));
}
判不为空
以前写法
1
2
3if(user!=null){
dosomething(user);
}JAVA8写法
1
2
3
4Optional.ofNullable(user)
.ifPresent(u->{
dosomething(u);
});
值判断
以前写法
1
2
3
4
5
6
7
8
9
10
11
12public User getUser(User user) throws Exception{
if(user!=null){
String name = user.getName();
if("zhangsan".equals(name)){
return user;
}
}else{
user = new User();
user.setName("zhangsan");
return user;
}
}java8写法
1
2
3
4
5
6
7
8
9public User getUser(User user) {
return Optional.ofNullable(user)
.filter(u->"zhangsan".equals(u.getName()))
.orElseGet(()-> {
User user1 = new User();
user1.setName("zhangsan");
return user1;
});
}