Java8深入学习之熟透Optional
一、使用Optional引言
1.1、代码问题引出
在写程序的时候一般都遇到过NullPointerException,所以经常会对程序进行非空的判断:
Useruser=getUserById(id); if(user!=null){ Stringusername=user.getUsername(); System.out.println("Usernameis:"+username);//使用username }
为了解决这种尴尬的处境,JDK终于在Java8的时候加入了Optional类,查看Optional的javadoc介绍:
Acontainerobjectwhichmayormaynotcontainanon-nullvalue.Ifavalueispresent,isPresent()willreturntrueandget()willreturnthevalue.
这是一个可以包含或者不包含非null值的容器。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
1.2、解决进阶
我们假设getUserById已经是个客观存在的不能改变的方法,那么利用isPresent和get两个方法,我们现在能写出下面的代码:
Optionaluser=Optional.ofNullable(getUserById(id)); if(user.isPresent()){ Stringusername=user.get().getUsername(); System.out.println("Usernameis:"+username);//使用username }
好像看着代码是优美了点,但是事实上这与之前判断null值的代码没有本质的区别,反而用Optional去封装value,增加了代码量。所以我们来看看Optional还提供了哪些方法,让我们更好的(以正确的姿势)使用Optional。
二、Optional三个静态构造方法
1)概述:
JDK提供三个静态方法来构造一个Optional:
1、Optional.of(Tvalue)
publicstaticOptional of(Tvalue){ returnnewOptional<>(value); }
该方法通过一个非null的value来构造一个Optional,返回的Optional包含了value这个值。对于该方法,传入的参数一定不能为null,否则便会抛出NullPointerException。
2、Optional.ofNullable(Tvalue)
publicstaticOptional ofNullable(Tvalue){ returnvalue==null?empty():of(value); }
该方法和of方法的区别在于,传入的参数可以为null——但是前面javadoc不是说Optional只能包含非null值吗?我们可以看看ofNullable方法的源码。
原来该方法会判断传入的参数是否为null,如果为null的话,返回的就是Optional.empty()。
3、Optional.empty()
publicstaticOptional empty(){ @SuppressWarnings("unchecked") Optional t=(Optional )EMPTY; returnt; }
该方法用来构造一个空的Optional,即该Optional中不包含值——其实底层实现还是如果Optional中的value为null则该Optional为不包含值的状态,然后在API层面将Optional表现的不能包含null值,使得Optional只存在包含值和不包含值两种状态。
2)分析:
前面javadoc也有提到,Optional的isPresent()方法用来判断是否包含值,get()用来获取Optional包含的值——值得注意的是,如果值不存在,即在一个Optional.empty上调用get()方法的话,将会抛出NoSuchElementException异常。
3)总结:
1)Optional.of(obj):它要求传入的obj不能是null值的,否则还没开始进入角色就倒在了NullPointerException异常上了.
2)Optional.ofNullable(obj):它以一种智能的,宽容的方式来构造一个Optional实例.来者不拒,传null进到就得到Optional.empty(),非null就调用Optional.of(obj).
那是不是我们只要用Optional.ofNullable(obj)一劳永逸,以不变应二变的方式来构造Optional实例就行了呢?那也未必,否则Optional.of(obj)何必如此暴露呢,私有则可。
三、Optional常用方法详解
3.1、Optional常用方法概述
Optional.of(Tt)
将指定值用Optional封装之后返回,如果该值为null,则抛出一个NullPointerException异常。
Optional.empty()
创建一个空的Optional实例。
Optional.ofNullable(Tt)
将指定值用Optional封装之后返回,如果该值为null,则返回一个空的Optional对象。
isPresent
如果值存在返回true,否则返回false
ifPresent
如果Optional实例有值则为其调用consumer,否则不做处理。
要理解ifPresent方法,首先需要了解Consumer类。简答地说,Consumer类包含一个抽象方法。该抽象方法对传入的值进行处理,但没有返回值。Java8支持不用接口直接通过lambda表达式传入参数。
如果Optional实例有值,调用ifPresent()可以接受接口段或lambda表达式。
Optional.get()
如果该值存在,将该值用Optional封装返回,否则抛出一个NoSuchElementException异常。
orElse(Tt)
如果调用对象包含值,返回该值,否则返回t。
orElseGet(Suppliers)
如果调用对象包含值,返回该值,否则返回s获取的值。
orElseThrow()
它会在对象为空的时候抛出异常。
map(Functionf)
如果值存在,就对该值执行提供的mapping函数调用。
flatMap(Functionmapper)
如果值存在,就对该值执行提供的mapping函数调用,返回一个Optional类型的值,否则就返回一个空的Optional对象。
3.2、Optional常用方法详解
3.2.1、ifPresent
publicvoidifPresent(Consumerconsumer){ if(value!=null) consumer.accept(value); }
如果Optional中有值,则对该值调用consumer.accept,否则什么也不做。
所以对于引言上的例子,我们可以修改为:
Optionaluser=Optional.ofNullable(getUserById(id)); user.ifPresent(u->System.out.println("Usernameis:"+u.getUsername()));
3.2.2、orElse
publicTorElse(Tother){ returnvalue!=null?value:other; }
如果Optional中有值则将其返回,否则返回orElse方法传入的参数。
Useruser=Optional .ofNullable(getUserById(id)) .orElse(newUser(0,"Unknown")); System.out.println("Usernameis:"+user.getUsername());
3.2.3、orElseGet
publicTorElseGet(Supplierother){ returnvalue!=null?value:other.get(); }
orElseGet与orElse方法的区别在于,orElseGet方法传入的参数为一个Supplier接口的实现——当Optional中有值的时候,返回值;当Optional中没有值的时候,返回从该Supplier获得的值。
Useruser=Optional .ofNullable(getUserById(id)) .orElseGet(()->newUser(0,"Unknown")); System.out.println("Usernameis:"+user.getUsername());
3.2.4、orElseThrow
publicTorElseThrow(SupplierexceptionSupplier)throwsX{ if(value!=null){ returnvalue; }else{ throwexceptionSupplier.get(); } }
orElseThrow与orElse方法的区别在于,orElseThrow方法当Optional中有值的时候,返回值;没有值的时候会抛出异常,抛出的异常由传入的exceptionSupplier提供。
举例说明:
在SpringMVC的控制器中,我们可以配置统一处理各种异常。查询某个实体时,如果数据库中有对应的记录便返回该记录,否则就可以抛出EntityNotFoundException,处理EntityNotFoundException的方法中我们就给客户端返回Http状态码404和异常对应的信息——orElseThrow完美的适用于这种场景。
@RequestMapping("/{id}") publicUsergetUser(@PathVariableIntegerid){ Optionaluser=userService.getUserById(id); returnuser.orElseThrow(()->newEntityNotFoundException("id为"+id+"的用户不存在")); } @ExceptionHandler(EntityNotFoundException.class) publicResponseEntity handleException(EntityNotFoundExceptionex){ returnnewResponseEntity<>(ex.getMessage(),HttpStatus.NOT_FOUND); }
3.2.5、map
publicOptionalmap(Functionmapper){ Objects.requireNonNull(mapper); if(!isPresent()) returnempty(); else{ returnOptional.ofNullable(mapper.apply(value)); } }
如果当前Optional为Optional.empty,则依旧返回Optional.empty;否则返回一个新的Optional,该Optional包含的是:函数mapper在以value作为输入时的输出值。
Stringusername=Optional.ofNullable(getUserById(id)) .map(user->user.getUsername()) .orElse("Unknown") .ifPresent(name->System.out.println("Usernameis:"+name));
而且我们可以多次使用map操作:
Optionalusername=Optional.ofNullable(getUserById(id)) .map(user->user.getUsername()) .map(name->name.toLowerCase()) .map(name->name.replace('_','')) .orElse("Unknown") .ifPresent(name->System.out.println("Usernameis:"+name));
3.2.6、flatMap
publicOptionalflatMap(Function>mapper){ Objects.requireNonNull(mapper); if(!isPresent()) returnempty(); else{ returnObjects.requireNonNull(mapper.apply(value)); } }
flatMap方法与map方法的区别在于,map方法参数中的函数mapper输出的是值,然后map方法会使用Optional.ofNullable将其包装为Optional;而flatMap要求参数中的函数mapper输出的就是Optional。
Optionalusername=Optional.ofNullable(getUserById(id)) .flatMap(user->Optional.of(user.getUsername())) .flatMap(name->Optional.of(name.toLowerCase())) .orElse("Unknown") .ifPresent(name->System.out.println("Usernameis:"+name));
3.2.7、filter
publicOptionalfilter(Predicatepredicate){ Objects.requireNonNull(predicate); if(!isPresent()) returnthis; else returnpredicate.test(value)?this:empty(); }
filter方法接受一个Predicate来对Optional中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个Optional;否则返回Optional.empty。
Optionalusername=Optional.ofNullable(getUserById(id)) .filter(user->user.getId()<10) .map(user->user.getUsername()); .orElse("Unknown") .ifPresent(name->System.out.println("Usernameis:"+name));
四、Optional使用示例
4.1、使用展示一
当user.isPresent()为真,获得它关联的orders的映射集合,为假则返回一个空集合时,我们用上面的orElse,orElseGet方法都乏力时,那原本就是map函数的责任,我们可以这样一行:
returnuser.map(u->u.getOrders()).orElse(Collections.emptyList()) //上面避免了我们类似Java8之前的做法 if(user.isPresent()){ returnuser.get().getOrders(); }else{ returnCollections.emptyList(); }
map是可能无限级联的,比如再深一层,获得用户名的大写形式:
returnuser.map(u->u.getUsername()) .map(name->name.toUpperCase()) .orElse(null);
以前的做法:
Useruser=..... if(user!=null){ Stringname=user.getUsername(); if(name!=null){ returnname.toUpperCase(); }else{ returnnull; } }else{ returnnull; }
filter():如果有值并且满足条件返回包含该值的Optional,否则返回空Optional。
OptionallongName=name.filter((value)->value.length()>6); System.out.println(longName.orElse("Thenameislessthan6characters"));
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对毛票票的支持。