目录
一、Optional的介绍
我们在编写代码的时候出现最多的就是空指针异常,所以在很多情况下我们需要做各种非空的判断,例如:
Author author = getAuthor();
if(author!=null){
System.out.println(author.getName());
}
尤其是对象中的属性还是一个对象的情况下,这种判断会更多,而过多的判断语句会让我们的代码显得臃肿不堪。
所以在JDK8中引入了Optional,养成使用Optional的习惯后,你可以写出更优雅的代码来避免空指针异常,并且在很多函数式编程相关的API中也都用到了Optional。
基本原理: 实际上optional相当于是一个包装类,它把数据封装成了optional对象当中的value属性,我们可以直接去调用optional相关的一些方法帮我们进行相应的非空判断、操作封装进去的数据,这样就可以非常优雅的避免空指针异常了。
接下来我们围绕着如何进行Optional对象的创建,以及如何安全的消费数据展开描述。
二、Optional的创建
1、Optional.ofNullable():推荐
我们一般使用Optional的静态方法ofNullable()方法来把数据封装成一个Optional对象。无论传入的参数是否为null都不会出现问题,把要封装的数据直接传入到参数当中就可以了,它就能得到对应的optional对象,它的泛型就是你传入的数据的类型:
Author author = new Author();
Optional<Author> authorOptional = Optional.ofNullable(author);
2、Optional.of():不推荐
如果你确定一个对象不是空的则可以使用Optional的静态方法of()来把数据封装成Optional对象,但是如果传入进of()方法的对象为空,也会报空指针异常:
Author author = new Author();
Optional<Author> authorOptional = Optional.of(author);
3、ofNullable()方法和of() 方法的区别:
可以看到,我们传入数据value之后,它会对ofNullable()方法内部会对这个数据进行是否为空的判断: 如果为空,它会调用empty()方法,Optional.empty()方法会把null封装成optional对象返回, 也就是获取到一个option的对象; 但是如果它不为空,它会调用of方法来封装数据:
4、ofNullable()和of() 等价的写法:
private static void test13(){
Optional<Author> authorOptional1 =getAuthorOptional1();
Optional<Author> authorOptional2 =getAuthorOptional2();
}
private static Optional<Author> getAuthorOptional1(){
Author author = new Author(1L,"蒙多",23,"一个从菜刀中明悟哲理的祖安人",null);
return Optional.ofNullable(author);
}
private static Optional<Author> getAuthorOptional2(){
Author author = new Author(1L,"蒙多",23,"一个从菜刀中明悟哲理的祖安人",null);
return author == null ? Optional.empty():Optional.of(author);
}
5、对Optional对象做进一步封装、提高代码复用性:
Author author = getAuthor();
optional<Author> authorOptional = optional.ofNullable(author);
private static Author getAuthor(){
Author author = new Author(1L,"蒙多",23,"一个从菜刀中明悟哲理的祖安人",null);
return author ;
//我们应该养成封装的习惯,返回Optional<Author>对象
}
你可能会觉得还要加一行代码来封装数据比较麻烦:
optional<Author> authorOptional = optional.ofNullable(author);
但是如果改造下getAuthor方法,让其返回值就是封装好的Optional的话,我们在使用时就会方便很多,推荐:
private static Optional<Author> getAuthorOptional1(){
Author author = new Author(1L,"蒙多",23,"一个从菜刀中明悟哲理的祖安人",null);
return Optional.ofNullable(author);
//直接返回一个Opptional<Author>对象,而不是Author对象
}
而且在实际开发中我们的数据很多是从数据库获取的,Mybatis从3.5版本可以也已经支持Optional了,我们可以直接把dao方法的返回值类型定义成optional类型,MyBastis会自己把数据封装成Optional对象返回,封装的过程也不需要我们自己操作。
三、Optional安全获取值
get()方法 orElseGet()方法 orElseThrow()方法:
1、get()方法:
如果我们期望安全获取值,不推荐使用get()方法获取,因为当Optional内部的数据为空的时候会出现异常:
而是用如下的两个方法:
2、orElseGet方法:
也可以用来获取值,并且当Optional对象内部的数据值为null的时候,你可以去设置一个默认值,如果当内部的数据值为null,它就会返回这个默认值。
当为null的时候:
private static void test13(){
Optional<Author> authorOptional =getAuthorOptional();
Author author = authorOptional.orElseGet(() -> new Author());
//当为null时就会new出来的Author对象进行返回
System.out.println(author);
}
private static Optional<Author> getAuthorOptional(){
Author author = new Author(1L,"蒙多",23,"一个从菜刀中明悟哲理的祖安人",null);
return Optional.ofNullable(null);
}
不为null的时候:
private static void test13(){
Optional<Author> authorOptional =getAuthorOptional();
Author author = authorOptional.orElseGet(() -> new Author());
//当为null时就会new出来的Author对象进行返回
System.out.println(author);
}
private static Optional<Author> getAuthorOptional(){
Author author = new Author(1L,"蒙多",23,"一个从菜刀中明悟哲理的祖安人",null);
return Optional.ofNullable(author);
}
3、orElseThrow方法:
获取数据,如果数据不为空则获取数据,如果为空则根据传入的参数来创建异常抛出,当我们使用spring这些框架的时候,当你抛出异常之后,我们可以使用spring去进行异常的统一的捕获,做统一的异常处理。
它里面其实也是需要一个Supplier接口,但是你要注意这个不太一样的地方,它的泛型是Throwable,它的返回值类型要求是一个Throwable:
private static void test14(){
Optional<Author> authorOptional =getAuthorOptional();
try {
Author author = authorOptional.orElseThrow(new Supplier<Throwable>() {
public Throwable get() {
return new RuntimeException("数据为null");
}
});
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
private static void test15(){
Optional<Author> authorOptional =getAuthorOptional();
try {
Author author = authorOptional.orElseThrow(()-> new RuntimeException("数据为null"));
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
如果为null:
四、Optional安全消费值
ifPresent()方法 推荐:
我们获取到一个Optional对象后肯定需要对其中的数据进行使用。这时候我们可以使用ifPresent()方法对Optional对象中的数据进行消费,如果Optional对象内部封装的数据不为空,不为空时才会执行具体的消费代码, 当数据为null的时候,这里面的数据消费操作是不会被执行的,这样就优雅的避免了空指针异常:
private static Optional<Author> getAuthor(){
Author author = new Author(1L,"蒙多",23,"一个从菜刀中明悟哲理的祖安人",null);
return Optional.ofNullable(author);
}
private static void test16(){
Optional<Author> author = getAuthor();
author.ifPresent(new Consumer<Author>() {
@Override
public void accept(Author author) {
System.out.println("作家姓名为:" + author.getName());
}
});
}
private static void test17(){
Optional<Author> author = getAuthor();
author.ifPresent(author1 -> System.out.println("作家姓名为:" + author1.getName()));
}
五、判断Optional内部是否存在数据
isPresent():
我们可以使用isPresent方法进行是否存在数据的判断。如果为空返回值为false,如果不为空,返回值为true。但是这种方式并不能体现Optional的好处,更推荐使用ifPresent方法:
private static Optional<Author> getAuthor(){
Author author = new Author(1L,"蒙多",23,"一个从菜刀中明悟哲理的祖安人",null);
return Optional.ofNullable(author);
}
private static void test18(){
Optional<Author> author = getAuthor();
if(author.isPresent()){
//如果Optional中没有数据,if判断为false的话,就不会执行if代码块了
System.out.println("作家姓名为:" + author.get().getName());
}
}
六、Optional中的过滤方法filter()
我们可以使用filter方法对数据进行过滤。如果原本是有数据的,但是不符合判断,也会变成一个无数据的Optional对象:
private static void test19() {
Optional<Author> author = getAuthor();
author.filter(author1 -> author1.getAge() < 18)
.ifPresent(author2 -> System.out.println("作家姓名为:" + author.get().getName()));
}
七、Optional中的数据类型转换和计算map
类似于filter,这个map和Stream流当中的map方法的作用也是类似的,我们可以对数据进行相应的计算或者进行类型的转换,并且最终类型转换完之后的数据也是被Optional对象所封装,保证了我们使用的安全。
Optional中的数据类型发生了变化:
private static void test21(){
Optional<Author> author = getAuthor();
Optional<List<Book>> optionalBooks = author.map(author1 -> author1.getBooks());
optionalBooks.ifPresent(books -> System.out.println(books));
}
private static void test21(){
Optional<Author> author = getAuthor();
author.map(author1 -> author1.getAge()+1000)
.ifPresent(age -> System.out.println(age + "============"));
}