java嵌套地狱_Java8:Optional - 解救分支地狱

相信任何一个Java开发者都会遇到NPE(即NullPointerException),而为了避免出现NPE,通常的方法是在访问对象前进行判空,即使用分支语句进行判断if (xx != null)。但是,过多的分支语句(尤其是和业务逻辑结合起来后),会导致代码可读性和可维护性的下降,因此需要减少它的出现。还好Java8提供了Optional这个工具,能够消除大部分判空和一部分普通判断。

Optional

Optional 代表一种有或为空的数据,使用Optional.ofNullable()可以创建一个接受null的Optional,而使用Optional.of()创建Optional时,传入参数为null就会立即抛出NPE。本文并非介绍Optional中各个方法的使用,而是在具体情形下的Optional取代if的实例。

替代判空分支

绝大部分的判空分支可以被Optional替代。

Object sth = ...;

// 为空抛出业务异常

if(sth == null){

throw new Exception...

}

// 或者提供默认值

if(sth == null){

sth = ...

}

// 从另外一个来源获取

if(sth == null){

sth = getFrom...()

}

// 不为空时,干点别的

if(sth != null){

...

}

// 不为空就,为空则(Java 9 以上适用)

if(sth != null){

...

}else{

...

}

复制代码

均可使用Optional的对应方法重构:

// 为空抛出业务异常

Optional.ofNullable(...).orElseThrow(() -> new Exception...);

// 或者提供默认值

Optional.ofNullable(...).orElse(xxx);

// 从另外一个来源获取

Optional.ofNullable(...).orElseGet(() -> getFrom...());

// 不为空时,干点别的

Optional.ofNullable(...).ifPresent(x -> ...);

// 不为空就,为空则(Java 9 以上适用)

Optional.ofNullable(...).ifPresentOrElse(x -> ...,() -> ...);

复制代码

安全链式调用

Object result = sth.getA().getB().getC().getD().getE();

复制代码

链式调用写起来确实爽,可是用着的时候其中如果有一个是null,那就要抓狂了😫,而且由于null的字段可能不确定,因此可能需要嵌套分支或者使用一个超长条件分支判空。

if(sth.getA() != null){

Object sth2 = sth.getA();

if(sth2.getB() != null){

...

}

}

// 或者

if(sth.getA() != null && sth.getA().getB() != null ...){

return ...

}

// 其他的不举例了

复制代码

使用Optional的map方法可以避免这一点:

Optional.ofNullable(sth).map(xxx::getA).map(yyy::getB)...

复制代码

map会自动对返回值包装成Optional,与map相对的还有flatMap方法,它只接受返回为Optional的方法引用。引入flatMap是为了防止出现多重包装,例如Optional。

public class X{

public Optional getName(){

return ...

}

}

X sth = ...

Optional.ofNullable(sth).flatMap(X::getName)...

复制代码

替代部分条件判断分支

Optional也能替代一些简单的Bean条件判断,利用的是filter这个方法。

XXX sth = ...

if(sth.getB() > 10){

return sth.getB().getC();

}

if(sth.getA() > 5){

throw new ...

}

// 使用Optional进行替代

// 注意这里这样子的话getC()的返回值就不能是null了,否则会抛出NoSuchElementException,要不把返回值类型改为Optional?

return Optional.of(sth).map(XXX::getB).filter(x -> x > 10).map(XXX:getB).get();

// 注意这里是orElse,所以filter要反过来!

Optional.of(sth).map(XXX::getA).filter(x -> x <= 5).orElseThrow(() -> new ...);

复制代码

使用if的优秀实践

即使是必须使用if的情况,也存在着一些技巧可以优化代码的结构。一个原则是:尽量减少代码嵌套,代码短的分支路径写在if下,必要时反转if。

一种思想是将if作为判断特殊条件的过滤器,及时判断「异常状态」并返回,以登录为例:

if(checkPassword(password,userInput)){

// 登录验证密码成功后可能会进行一堆操作

...

}else{

// 如果上面的代码非常多,不回顾下不知道是什么的else

throw new Exception("密码错误")

}

// 但如果是下面的写法,可能会好多了

if(!checkPassword(password,userInput)){

// 代码短的分支路径写在if下

// 调转if的条件,把验证密码错误作为一个特例进行过滤

throw new Exception("密码错误")

}

// 登录验证密码成功后的一堆操作,就不在分支里面,当作正常处理

...

复制代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值