1 Optional介绍
在日常开发中,NullPointerException相信所有人都见过,不管你是刚入行的萌新还是骨灰级玩家,对于它都是耳熟能详的。它的出现可以说无处不在,总是能在各种场景下出现。那么对于如何防止它的出现,我们平时都是被动的采用各种非空校验,但是它还是经常能出现在我们的视线中。
public String getCompanyName(Student student){ if (student != null){ Job job = student.getJob(); if (job != null){ Company company = job.getCompany(); if (company != null){ String name = company.getName(); return name; }else { return "no company"; } }else { return "no job"; } }else { return "no student"; } }
对于上述这段代码,相信大家平时工作类似的代码经常会有出现。每一次在获取到一个对象时都进行一个null的判断,然后才继续后续的实现。但是这种方式很不好,首先会存在大量的if-else判断嵌套,导致代码的可读性和扩展性极差。此时,有的同学可能就会这么改造,如下所示:
public String getCompanyName(Student student){ if (student == null){ return "no student"; } Job job = student.getJob(); if (job == null){ return "no job"; } Company company = job.getCompany(); if (company == null){ return "no company"; } return company.getName(); }
这种判断已经有意识的避免了大量嵌套判断,但是同样存在多个不同的判断点,代码维护同样困难。
那么有没有一种方式可以优雅的解决这些问题呢?
2 Optional应用
为了防止空指针异常的出现,Java8中引入了一个新类Optional,对于它之前我们已经进行了简单的实现。其本质就是通过Optional类对值进行封装, 当有值的时候,会把该值封装到Optional类中。如果没有值的话,则会在该类封装一个Empty
在Optional类中基于函数式接口提供了一些用于操作值的方法。
2.1 创建Optional对象
要创建Optional,该类提供了三种方法操作,分别为:
empty()、of()、ofNullable()。使用方式如下所示:
Optional<Student> studentOptional =Optional.empty(); Optional<Student> studentOptional =Optional.of(student); Optional<Student> studentOptional =Optional.ofNullable(student);
可以看到这三个方法,都会返回Optional对象,那么三者之间有什么区别呢?根据源码分析如下:
根据源码可知,empty()会直接返回一个空的Optional实例,内部不会存在任何值。
根据源码可知,of()会返回一个存在值的Optional对象,并且该值不允许null的存在。如果调用该方法时传入参数是null,则立刻抛出NullPointerException,而不是等到你用这个对象时才抛出,相当于进行了立即检查。
根据源码可知,ofNullable()同样也会返回一个存在值的Optional对象,但是它和of()最大的不同在于,它会对传入的值进行判断,如果传入的值为null,其会调用empty()返回一个不包含内容的Optional,如果不为null,则会调用of()返回一个包含内容的Optional
2.2 基于Optional对象获取值
有了Optional对象之后,就需求获取其内部的值了,Optional类也提供了多种方法用于值的获取。
2.2.1 isPresent()与ifPresent()应用&源码解析
Optional类中提供了两个方法用于判断Optional是否有值,分别是isPresent()和ifPresent(Consumer<? super T>consumer)。其一般与ofNullable()搭配使用,因为of()在创建时已经完成了判断,而empty()只是单纯了实例化了一个Optional对象。
根据源码可知,isPresent()内部非常简单,就是判断这个值是否为null。
根据源码可知,该方法在执行时,接收一个consumer函数式接口,如果value不为null,则通过consumer中的accept方法获取该值。
使用示例如下:
public class PresentDemo { public static void getStudentName(Student student){ Optional<Student> studentOptional = Optional.ofNullable(student); if (studentOptional.isPresent()){ //存在 System.out.println("student存 在"); }else { System.out.println("student不存 在"); } } public static void main(String[] args) { Student student = new Student(1,"zhangsan","M"); getStudentName(student); } } Optional<Student> studentOptional = Optional.ofNullable(student); studentOptional.ifPresent(s-> System.out.println("学生存在"));
2.2.2 get()应用&源码解析
get()的使用非常简单,但不安全,因为其在获取值的时候,如果值存在,则直接返回封装在Optional中的值,如果不存在,则抛出NoSuchElementException。因此它的使用前提是已经确定Optional中有值,否则没有使用意义。
使用示例如下:
Optional<Student> studentOptional = Optional.ofNullable(student); if (studentOptional.isPresent()){ Student result = studentOptional.get(); }
2.2.3 orElseThrow()应用&源码解析
该方法与get()类似,都是用于取值,但是当Optional中没有值时,get()会直接抛出
NoSuchElementException,这样的话,就存在了一定的局限性,因为有时可能需要抛出自定义异常。此时就可以使用orElseThrow(),它在取值时,如果Optional中没有值时,可以抛出自定义异常。
public class MyException extends Throwable { public MyException() { super(); } public MyException(String message) { super(message); } @Override public String getMessage() { return "exception message"; } }
public class OrElseThrowDemo { public static void getStudentInfo(Student student) { Optional<Student> studentOptional = Optional.ofNullable(student); try { Student student1 = studentOptional.orElseThrow(MyException::new ); } catch (MyException e) { e.printStackTrace(); } } public static void main(String[] args) { Student student = null; getStudentInfo(student); } }
2.2.4 map()应用&源码解析
当Option中有值的话,经常性的一个操作就是从值中获取它的某一个属性,实例如下:
if(job != null){ String name = job.getName(); }
对于这种需求,可以通过map()完成,它的使用思路与Stream中的map类似,只不过一个是转换Stream的泛型,一个是转换Optional的泛型。
使用示例如下:
if (studentOptional.isPresent()){ Optional<String> nameOptional = studentOptional.map(Student::getName); }