对于Java程序员来说,null是令人头痛的东西。时常会受到空指针异常(NPE)的骚扰。连Java的发明者都承认这是他的一项巨大失误。 那么,有什么办法可以避免在代码中写大量的判空语句呢? 有人说可以使用JDK8提供的Optional来避免判空,但是用起来还是有些麻烦。 作者在日常工作中,封装了一个工具,可以可以链式调用对象成员而无需判空,相比原有的ifnull逻辑和JDK8提供的Optional更加优雅易用,在工程实践中大大提高了编码效率,也让代码更加的精准和优雅。 不优雅的判空调用 我想从事Java开发的小伙伴肯定有遇到过下面这种让人难受的判空逻辑:现在有一个User类,School是它的成员变量authorAxinsince20200920summary一个User类定义(Ps:Data是lombok组件提供的注解,简化了getset等等的约定代码)DatapublicclassUser{privateStringname;privateStringgender;privateSchoolschool;DatapublicstaticclassSchool{privateStringscName;privateStringadress;}}复制代码 现在想要获得School的成员变量adress,一般的处理方式:publicstaticvoidmain(String〔〕args){UseraxinnewUser();User。SchoolschoolnewUser。School();axin。setName(hello);if(Objects。nonNull(axin)Objects。nonNull(axin。getSchool())){User。SchooluserScaxin。getSchool();System。out。println(userSc。getAdress());}}复制代码 获取adress时要对School进行判空,虽然有些麻烦,到也能用,通过JDK8提供的Optional工具也是可以,但还是有些麻烦。 而下文的OptionalBean提供一种可以链式不断地调用成员变量而无需判空的方法,直接链式调用到你想要获取的目标变量,而无需担心空指针的问题。 链式调用成员变量 如果用了本文设计的工具OptionalBean,那么上述的调用可以简化成这样:publicstaticvoidmain(String〔〕args){UseraxinnewUser();User。SchoolschoolnewUser。School();axin。setName(hello);1。基本调用Stringvalue1OptionalBean。ofNullable(axin)。getBean(User::getSchool)。getBean(User。School::getAdress)。get();System。out。println(value1);}复制代码 执行结果: 其中User的school变量为空,可以看到代码并没有空指针,而是返回了null。这个工具怎么实现的呢? OptionalBean工具authorAxinsince20200910summary链式调用bean中value的方法publicfinalclassOptionalBeanT{privatestaticfinalOptionalBeanlt;?EMPTYnewOptionalBean();privatefinalTvalue;privateOptionalBean(){this。valuenull;}空值会抛出空指针paramvalueprivateOptionalBean(Tvalue){this。valueObjects。requireNonNull(value);}包装一个不能为空的beanparamvalueparamTreturnpublicstaticTOptionalBeanTof(Tvalue){returnnewOptionalBean(value);}包装一个可能为空的beanparamvalueparamTreturnpublicstaticTOptionalBeanTofNullable(Tvalue){returnvaluenull?empty():of(value);}取出具体的值paramfnparamRreturnpublicTget(){returnObjects。isNull(value)?null:value;}取出一个可能为空的对象paramfnparamRreturnpublicROptionalBeanRgetBean(Functionlt;?superT,?extendsRfn){returnObjects。isNull(value)?OptionalBean。empty():OptionalBean。ofNullable(fn。apply(value));}如果目标值为空获取一个默认值paramotherreturnpublicTorElse(Tother){returnvalue!null?value:other;}如果目标值为空通过lambda表达式获取一个值paramotherreturnpublicTorElseGet(Supplierlt;?extendsTother){returnvalue!null?value:other。get();}如果目标值为空抛出一个异常paramexceptionSupplierparamXreturnthrowsXpublicXextendsThrowableTorElseThrow(Supplierlt;?extendsXexceptionSupplier)throwsX{if(value!null){returnvalue;}else{throwexceptionSupplier。get();}}publicbooleanisPresent(){returnvalue!null;}publicvoidifPresent(Consumerlt;?superTconsumer){if(value!null)consumer。accept(value);}OverridepublicinthashCode(){returnObjects。hashCode(value);}空值常量paramTreturnpublicstaticTOptionalBeanTempty(){SuppressWarnings(unchecked)OptionalBeanTnone(OptionalBeanT)EMPTY;returnnone;}}复制代码 工具设计主要参考了Optional的实现,再加上对链式调用的扩展就是上述的OptionalBean。 getBean其实是当变量为空时返回了一个包装空值的OptionalBean对象,同时泛型的使用让工具更加易用。 使用手册 可以看到代码中也提供了和Optional一样的扩展方法,如ifPresent()、orElse()等等:publicstaticvoidmain(String〔〕args){UseraxinnewUser();User。SchoolschoolnewUser。School();axin。setName(hello);1。基本调用Stringvalue1OptionalBean。ofNullable(axin)。getBean(User::getSchool)。getBean(User。School::getAdress)。get();System。out。println(value1);2。扩展的isPresent方法用法与Optional一样booleanpresentOptionalBean。ofNullable(axin)。getBean(User::getSchool)。getBean(User。School::getAdress)。isPresent();System。out。println(present);3。扩展的ifPresent方法OptionalBean。ofNullable(axin)。getBean(User::getSchool)。getBean(User。School::getAdress)。ifPresent(adressSystem。out。println(String。format(地址存在:s,adress)));4。扩展的orElseStringvalue2OptionalBean。ofNullable(axin)。getBean(User::getSchool)。getBean(User。School::getAdress)。orElse(家里蹲);System。out。println(value2);5。扩展的orElseThrowtry{Stringvalue3OptionalBean。ofNullable(axin)。getBean(User::getSchool)。getBean(User。School::getAdress)。orElseThrow(()newRuntimeException(空指针了));}catch(Exceptione){System。out。println(e。getMessage());}}复制代码 run一下: 总结 设计了一种可以链式调用对象成员而无需判空的工具让代码更加的精准和优雅,如果本文设计的工具满足了刚好解决你的困扰,那就在项目中使用吧!