防止 NPE ,是程序员的基本修养,注意 NPE 产生的场景,使用 JDK8 的 Optional 类来防止 NPE 问题。
Optional 类是Java8为了解决null值判断问题,借鉴google guava类库的Optional类而引入的一个同名Optional类,使用Optional类可以避免显式的null值判断(null的防御性检查),避免null导致的NPE(NullPointerException)。
Optional类的两个构造方法都是private型的,因此类外部不能显示的使用new Optional()的方式来创建Optional对象,但是Optional类提供了三个静态方法 empty()、of(T value)、ofNullable(T value) 来创建Optinal对象,示例如下:
package test;
import java.util.Optional;
public class MainTest {
public static void main(String[] args) {
// 1、创建一个包装对象值为空的Optional对象
Optional<Student> studentOpt = Optional.empty();
// 2、创建包装对象值非空的Optional对象
Optional<Student> studentOpt1 = Optional.of(new Student());
// 3、创建包装对象值允许为空的Optional对象
Optional<Student> studentOpt2 = Optional.ofNullable(null);
System.out.println(studentOpt.get().getName()); // 打印:异常,Exception in thread "main" java.util.NoSuchElementException: No value present
System.out.println(studentOpt.orElse(new Student()).getName()); // 打印:测试
System.out.println(studentOpt1.get().getName()); // 打印:测试
System.out.println(studentOpt2.get().getName()); // 打印:异常,Exception in thread "main" java.util.NoSuchElementException: No value present
System.out.println(studentOpt2.orElse(new Student()).getName()); // 打印:测试
}
}
class Student {
private String sex = "男";
private String name = "测试";
public String getName() {
return name;
}
public String getSex() {
return sex;
}
}
上图的运行示例可以看出,Optional 可以创建任意类型的对象,并且通过 get()方法获取特定的对象,创建 Optional 为空时,获得的对象就是空的,但是可以通过 ** orElse**来判断,如果为空,再通过orElse 的参数重新创建。
System.out.println(studentOpt.isPresent()); // 打印:false
System.out.println(studentOpt1.isPresent()); // 打印:true
isPresent() 方法用于判断 Optional 封装的对象是否为空。
Student studentNull = null;
Student student = new Student();
Optional.ofNullable(studentNull).ifPresent(u -> System.out.println("这个对象是 null 的"));
Optional.ofNullable(student).ifPresent(u -> System.out.println("这个对象是不是NULL的"));
// 打印结果:这个对象是不是NULL的
ifPresent() 方法接受一个Consumer对象(消费函数),如果包装对象的值非空,运行Consumer对象的accept()方法。这样的话就可以愉快的用 Lambda 表达式了
Student studentNull = null;
Student student = new Student();
Optional.ofNullable(studentNull).filter(u -> "测试".equals(u.getName())).ifPresent(u -> System.out.println("studentNull,名字与“测试”目标相等"));
Optional.ofNullable(student).filter(u -> "测试".equals(u.getName())).ifPresent(u -> System.out.println("student,名字与“测试”目标相等"));
Optional.ofNullable(student).filter(u -> "不测试不测试".equals(u.getName())).ifPresent(u -> System.out.println("student,名字与“不测试不测试”目标相等"));
// 打印结果:student,名字与“测试”目标相等
filter()方法也是非常实用的,这个方法用于对 Optional 对象进行过滤,如果符合条件的,返回Optional对象本身,否则返回一个空的Optional对象。
System.out.println(Optional.ofNullable(student).map(u -> u.getName()).get());
System.out.println(Optional.ofNullable(studentNull).map(u -> u.getName()));
/*
* 打印结果
测试
Optional.empty
* */
map()方法的参数为Function(函数式接口)对象,map()方法将Optional中的包装对象用Function函数进行运算,并包装成新的Optional对象(包装对象的类型可能改变)。
上述代码中,先用ofNullable()方法构造一个Optional对象,返回Optional对象(如果 student 为 null, 返回 map() 方法返回一个空的 Optinal 对象)。
System.out.println(Optional.ofNullable(student).map(u -> u.getName()).get());
System.out.println(Optional.ofNullable(studentNull).map(u -> u.getName()).get());
/*
* 打印结果
测试
Exception in thread "main" java.util.NoSuchElementException: No value present
at java.base/java.util.Optional.get(Optional.java:141)
at test.MainTest.main(MainTest.java:21)
* */
运行上面两条语句,可以发现同样出现了空指针异常,这样的用法还是没有避免 NPE 的产生,因为Optional.ofNullable(studentNull).map(u -> u.getName()) 避免了 studentNull 是空带来的异常,但是 u.getName() 依旧获取不到正常的值(实际上是这个 Optional.empty),所以在get()时出现异常,这样用起来还是不爽,所有需要另一个方法来解决这个问题。我们对上面的代码进行修改。
System.out.println(Optional.ofNullable(student).map(u -> u.getName()).orElse("这个可能是空"));
System.out.println(Optional.ofNullable(studentNull).map(u -> u.getName()).orElse("这个一定是空"));
/*
* 打印结果
测试
这个一定是空
* */
用 orElse 方法替代 get 方法,orElse()方法功能比较简单,即如果包装对象值非空,返回包装对象值,否则返回入参other的值(默认值)。
相似的还有 orElseGet() 方法,区别在于 orElseGet() 方法的入参为一个Supplier对象,用Supplier对象的get()方法的返回值作为默认值。如:
public static String getGender(Student student) {
return Optional.ofNullable(student).map(u -> u.getGender()).orElseGet(() -> "Unkown");
}
文章参考:https://www.jianshu.com/p/d81a5f7c9c4e