当前位置: 首页 > 后端技术 > Java

可选是个好东西,你真的知道怎么用吗?

时间:2023-04-01 16:06:11 Java

引言文章开头,先说说NPE问题。NPE问题就是我们在开发中经常遇到的NullPointerException。假设我们有两个类,它们的UML类图如下图所示。在这种情况下,有以下代码user.getAddress().getProvince();这样写,当user为null时,就有可能报NullPointerException。为了解决这个问题,使用了下面的写法if(user!=null){Addressaddress=user.getAddress();if(address!=null){Stringprovince=address.getProvince();}}这种写法是Ugly,为了避免上面写的丑陋,让丑陋的设计变得优雅。JAVA8提供了Optional类来优化这种写法。下面的文字部分将详细描述API。API介绍先介绍一下API。与其他文章不同的是,本文采用类比的方式,并结合源码。与其他文章不同的是,API是一一罗列的,让人找不到重点。1、Optional(Tvalue)、empty()、of(Tvalue)、ofNullable(Tvalue)这四个函数是相互关联的,所以存放在一组中。先说明一下,Optional(Tvalue),也就是构造函数,是有私有权限的,不能被外部调用。其余三个函数是公有权限供我们调用。那么,Optional的本质就是里面存放了一个真实的值,构造的时候直接判断它的值是否为空。嗯,还是比较抽象。直接上Optional(Tvalue)构造函数的源码,如下图,那么,of(Tvalue)的源码如下publicstaticOptionalof(Tvalue){返回新的可选<>(值);}也就是说在of(Tvalue)函数内部调用了构造函数。根据构造函数的源码,我们可以得出两个结论:通过of(Tvalue)函数构造的Optional对象,在Value值为空时,依然会报NullPointerException。of(Tvalue)函数构造的Optional对象在Value值不为空的情况下可以正常构造Optional对象。另外,Optional类还维护了一个值为null的对象,大概就是下面的publicfinalclassOptional{//省略....privatestaticfinalOptionalEMPTY=newOptional<>();privateOptional(){this.value=null;}//省略...publicstaticOptionalempty(){@SuppressWarnings("unchecked")Optionalt=(Optional)EMPTY;返回吨;}}然后,empty()的作用就是返回EMPTY对象。好了,铺垫了这么多,可以说ofNullable(Tvalue)起作用了。源代码是publicstaticOptionalofNullable(Tvalue){returnvalue==null?空():(值);}嗯,大家应该明白什么意思了吧。与of(Tvalue)相比,不同的是当value为null时,of(Tvalue)会报NullPointerException;ofNullable(Tvalue)不会抛出Exception,ofNullable(Tvalue)会直接返回一个EMPTY对象。那是不是意味着我们在项目中只使用ofNullable函数,而不使用of函数呢?不,事物存在,自然就有存在的价值。当我们运行时,我们不想隐藏NullPointerException。相反,立即报告,在这种情况下使用Of函数。但不得不承认,这样的场景真的很少见。博主只在写junit测试用例的时候用过这个功能。2、orElse(Tother)、orElseGet(Supplierother)和orElseThrow(SupplierexceptionSupplier)这三个函数存放在一个组中,构造函数传入的值为null时调用.orElse和orElseGet的用法如下,当值为null时相当于给定一个默认值:@Testpublicvoidtest(){Useruser=null;user=Optional.ofNullable(user).orElse(createUser());user=Optional.ofNullable(user).orElseGet(()->createUser());}publicUsercreateUser(){用户user=newUser();user.setName("张三");returnuser;}这两个函数的区别:当user值不为null时,orElse函数还是会执行createUser()方法,但是orElseGet函数不会执行createUser()方法,大家可以测试一下你自己。至于orElseThrow,当值为null时,直接抛出异常。用法如下Useruser=null;Optional.ofNullable(user).orElseThrow(()->newException("Userdoesnotexist"));3,map(Functionmapper)和flatMap(Function>mapper)放在一组内存中,这两个函数用来进行值转换操作。直接看源码publicfinalclassOptional{//省略....publicOptionalmap(Functionmapper){Objects.requireNonNull(mapper);如果(!isPresent())返回空();else{returnOptional.ofNullable(mapper.apply(value));}}//省略...publicOptionalflatMap(Function>mapper){Objects.requireNonNull(mapper);如果(!isPresent())返回空();else{returnObjects.requireNonNull(mapper.apply(value));}}}这两个函数,在函数体中没有区别。唯一的区别是输入参数。map函数接受的入参类型为Function,而flapMap的入参类型是Function>。具体用法上,对于map:如果User结构是下面的publicclassUser{privateStringname;publicStringgetName(){返回名称;}}那么名字写成如下Stringcity=Optional.ofNullable(user).map(u->u.getName()).get();对于flatMap:如果User结构如下publicclassUser{privateStringname;publicOptionalgetName(){returnOptional.ofNullable(name);}}这时候名字的写法如下Stringcity=Optional.ofNullable(user).flatMap(u->u.getName()).get();4.isPresent()和ifPresent(Consumerconsumer)这两个函数放在一起记住,isPresent是判断值是否为空,ifPresent是值不为空时做一些操作。这两个函数的源码如下publicfinalclassOptional{//省略....publicbooleanisPresent(){returnvalue!=null;}//省略...publicvoidifPresent(Consumerconsumer){if(value!=null)consumer.accept(value);}}需要补充的是不能写if(user!=null){//TODO:dosomething}asUseruser=Optional.ofNullable(user);if(Optional.isPresent()){//TODO:dosomething}正因为如此,代码结构仍然很难看。后面博主会给出正确的写法。至于ifPresent(Consumerconsumer),用法也很简单,如下图Optional.ofNullable(user).ifPresent(u->{//TODO:dosomething});5,filter(Predicatepredicate)话不多说,直接上传源码publicfinalclassOptional{//省略....Objects.requireNonNull(predicate);如果(!isPresent())返回这个;否则返回predicate.test(value)?this:empty();}filter方法接受一个Predicate来过滤Optional中包含的值。如果包含的值满足条件,则返回这个Optional;否则,返回可选。空的。用法如下Optionaluser1=Optional.ofNullable(user).filter(u->u.getName().length()<6);如上所示,如果用户名长度小于6,则返回。如果大于6,则返回EMPTY对象。写前函数方法中的实际使用示例1publicStringgetCity(Useruser)throwsException{if(user!=null){if(user.getAddress()!=null){if(address.getCity()!=null){返回address.getCity();}}}thrownewExcpetion("值错误");}JAVA8写法publicStringgetCity(Useruser)throwsException{returnOptional.ofNullable(user).map(u->u.getAddress()).map(a->a.getCity()).orElseThrow(()->newException("Instructionfetcherror"));}例2比如在主程序中,前面的写法if(user!=null){dosomething(user);}JAVA8的写法Optional。ofNullable(user).ifPresent(u->{dosomething(u);});例3之前的写法publicUsergetUser(Useruser)throwsException{if(user!=null){Stringname=user.getName();if("zhangsan".equals(name)){返回用户;}}else{user=newUser();用户.setName("张三");返回用户;}}java8notationpublicUsergetUser(Useruser){returnOptional.ofNullable(user).filter(u->"zhangsan".equals(u.getName())).orElseGet(()->{Useruser1=newUser();user1.setName("zhangsan");returnuser1;});}其他的例子,就不一一列举了,但是用这个链式编程,虽然代码优雅起来了。但是逻辑不是那么明显,可读性降低了。大家可以根据自己项目中的情况使用。作者:zjhredblog.csdn.net/zjhred/article/details/84976734