Java如何进行优雅的判空——Optional类的灵活应用

0 引言

在Java Web项目开发中,经常令人头疼的NPE问题(NullPointerException)——空指针,例如我们在调用equal()方法时,就经常会出现NPE问题:

String str  = null;
str.equals("fsfs")

上边写法就会出现NPE问题,即采用一个空的字符串str调用equals(),相当于null.equals()。

1. if-else判空

if-else判空是我们经常使用的解决NPE问题的方式,上述代码即可改为:

String str = null;
 // 判断是否为null,不是在进行比较
if (str != null){
    
    
	str.equals("fsfs);
}

这种写法是比较丑陋的,为了避免上述丑陋的写法,让丑陋的设计变得优雅。Java8 提供了Optional类来优化这种写法,接下来的正文部分进行详细说明。

2. Optional类判空

2.1 Optional类简介

在Optional类中,构造方法是私有的,本身也被final修饰,因此无法被继承和直接new对象,但是Optional类提供的三个public静态方法,实现创建对象的过程。

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

2.2 静态方法 empty(),of(T value),ofNullable(T value)

2.2.1 of(T value)源码解析
private final T value;

public static <T> Optional<T> of(T value) {
    
    
    return new Optional<>(value);
}
// 私有构造方法,要求非空值
private Optional(T value) {
    
    
    this.value = Objects.requireNonNull(value);
}
  • 从源码中可以看见,静态方法of(T value)内部调用了私有的构造方法创建Optional对象,在调用构造方法时,通过Objects.requireNonNull(value)方法判断传入的value值,当Value值为空时,会报NullPointerException;
  • 因此在调用of(T value) 方法时,传入的value必须不为null,才能返回一个携带value的Optional对象;
2.2.2 empty() 源码解析
private static final Optional<?> EMPTY = new Optional<>();

public static<T> Optional<T> empty() {
    
    
    @SuppressWarnings("unchecked")
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
}
  • 调用empty()时不用传入值,直接会返回一个Optional对象
2.2.3 ofNullable(T value)源码解析(项目中常用
public static <T> Optional<T> ofNullable(T value) {
    
    
    return value == null ? empty() : of(value);
}
  • ofNullable(T value)方法解决了of(T value)必须传入非空值的要求,当传入的value 为null时,调用empty()方法创建一个Optional对象,不为null时,调用of(T value)创建一个Optional对象;

2.3 orElse(T other),orElseGet(Supplier other)和orElseThrow(Supplier exceptionSupplier)

这三个函数放一组进行记忆,都是在构造函数传入的value值为null时,进行调用的。

2.3.1 orElse和orElseGet

用法如下所示,相当于value值为null时,给予一个默认值:

@Test
public void test() {
    
    
    User user = null;
    user = Optional.ofNullable(user).orElse(createUser());
    user = Optional.ofNullable(user).orElseGet(() -> createUser());

}
public User createUser(){
    
    
    User user = new User();
    user.setName("zhangsan");
    return user;
}
  • orElse(T other),orElseGet(Supplier other)的区别是:当user值不为null时,orElse函数依然会执行createUser()方法,而orElseGet函数并不会执行createUser()方法
2.3.2 orElseThrow(Supplier<? extends X> exceptionSupplier)

就是value值为null时,直接抛一个异常出去,用法如下所示

User user = null;
Optional.ofNullable(user).<RuntimeException>orElseThrow(()-> new RuntimeException("用户不存在"));
  • 注意:在使用orElseThrow()时,需要将后边抛出的异常RuntimeException,以泛型方式写在orElseThrow方法前边,如下所示:
<RuntimeException>orElseThrow(()-> new RuntimeException("用户不存在"))

2.4 filter(Predicate<? super T> predicate), map(Function mapper) 和 flatMap(Function mapper)

这三个方法的用法可Stream流处理中对应的方法类似:

2.4.1 filter(Predicate<? super T> predicate)

filter 方法接受一个 Predicate 来对 Optional 中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个 Optional;否则返回 Optional.empty。

Optional<User> user1 = Optional.ofNullable(user).filter(u -> u.getName().length()<6);
2.4.2 map(Function mapper)
String city = Optional.ofNullable(user).map(u-> u.getName()).get();
2.4.3 flatMap(Function mapper)

flatMap都是获取

String city = Optional.ofNullable(user).flatMap(u-> u.getName()).get();

map 和 flatMap这两个函数,在函数体上没什么区别。唯一区别的就是入参,map函数所接受的入参类型为Function<? super T, ? extends U>,而flapMap的入参类型为Function<? super T, Optional>。

猜你喜欢

转载自blog.csdn.net/qq_42102911/article/details/129025220