专栏首页dylanliu使用Optional来减少null检查

使用Optional来减少null检查

由来

平常我们使用null检查在项目中简直太常见了,从数据库中查询到的数据可能不存在返回null,service中处理中发现不存在返回一个null,在互相调用的时候每次都需要做(if obj != null)的判断,散落在程序中很难看。更难看的是当你遗漏了一个空指针判断,程序就会无情的给你抛出一个NPE让你知道谁才是老大。

假设我们有一个用户类User,用户可以有收货地址类Addr,收货地址中肯定会有省province属性啦,如果我们要获取用户的收货省,简单的来一个链式操作user.getAddr().getProvince(),这样操作可以么?

以往的null检查方式

用户在新注册之后可能是没有收货地址的,因此user.getAddr()返回null,再调用就会给你点颜色看看。因此我们必须要先判断null,然后才能决定是否可以调用getProvince方法

public String getUserConsigneeProvince(User user){
  if (user != null){
     Addr addr = user.getAddr();
     if (addr != null){
       return addr.getProvince();
     }
  }
  return null;  
}

或者使用防御式编程方式(以前我喜欢的编程方式),在检查到null后直接处理

public String getUserConsigneeProvince(User user){
  if (user == null){
    return null;
  }
  Addr addr = user.getAddr();
  if (addr == null){
    return null;
  }
  return addr.getProvince();
}

在这里我们也很鸡贼的返回了一个null来表示用户的收货省不存在,给以后的使用这个方法的人(当然也包括自己)挖了一个坑,如果直接返回给前端,那页面上就会有一个大大的null等着QA给你提bug吧。

1.8中对Null的处理

在Haskell中有一个Maybe类来处理可能的null,Scala中也提供了Option[T]来表示,Kotlin中使用在调用后加?来安全的处理返回值为null的情况。Java1.8借鉴了Haskell和Scala中方式,提供了一个Optional类来帮助程序员避免null检查。

设计哲学

我们看到在获取收货省的api中返回值直接是一个String,我们是不可能从这个返回值上面看出用户的收货省是否存在的,因此在设计时,对于可能不存在的值,我们选择返回一个Optional来表示你需要处理不存在的情况。

因此我们把返回值改写成Optional<String>

public Optional<String> getUserConsigneeProvince(User user){
  if (user == null){
    return Optional.empty();
  }
  Addr addr = user.getAddr();
  if (addr == null){
    return Optional.empty();
  }
  return Optional.of(addr.getProvince());
}

不过这并没有改善我们的内部处理,还是判断了null,因此我们先来看一下Optional的API,来来看一下用Optional如何简化我们的书写方式。

处理方式

API

Optional<T> Api:

名称

返回值

参数

说明

isPresent

boolean

void

如果不存在值则false,存在为true

ifPresent

void

Consumer

如果存在则调用Consumer消费值

map

Function

Optional<U>

对值做映射

flatMap

Function

Optional<U>

对值做扁平映射

orElse

T

T

存在返回包含的值,不存在就返回这里面的值

orElseGet

T

Supplier<T>

返回用supplier 生成的一个值

filter

Predicate

Optional<T>

过滤值

get

T

void

获取包装的值,不存在会抛出异常

可以看到API设计中使用到了函数式相关的东西,使得我们调用的时候可以使用lambda或者行为参数化的方式更方便的使用 在map和flatMap等API中隐含了null的判断,使得我们不用在应用中显式的去做null判断了。

一个参数的实践

接下来就是见证奇迹的时刻:

public Optional<String> getUserConsigneeProvince(User user){
  return Optional.ofNullable(user).map(User::getAddr).map(Addr::getProvince);
}

我们使用1行代码代替了6行,而且表达的更加清晰

当然如果这个API很多人使用,很难改变返回值的话我们可以使用orElse做值处理,如下:

public String getUserConsigneeProvince(User user){
  return Optional.ofNullable(user).map(User::getAddr).map(Addr::getProvince).orElse("");
}

get方法只有我们在确定值一定存在的情况下才能使用。

可以看到我们并未改变接口参数和返回值,但在内部处理上经过重写我们已经简化了非常多的代码,逻辑也变得清晰,更具有可读性。

为何可以如此写呢?Optional类其实是将null判断内化了,将null判断从用户手中接过来变成自己API的一部分,把用户从null判断的深渊中解放出来,只用关注自己的业务处理逻辑。

两个参数的处理

上面是一个参数的处理,如果我们有两个参数该怎么办呢。

假设有两个用户,我们需要判断一下他们是否是同一个省的,就使用上面已经提供的获取省的方法

public static boolean isSameProvince(User user1, User user2) {
    String user1Province = getUserConsigneeProvince(user1);
    String user2Province = getUserConsigneeProvince(user2);
    if (user1Province == null || user2Province == null) {
        return false;
    }
    if (user2Province.equals(user2Province)) {
        return true;
    }
    return false;
}

还好还好,只有一个if判断了null的情况,可是我们也用了9行才完成了这个简单的功能,我们其实最需要的equals这个判断,上面的三行null相关应该是需要避免的。 我们用Optional来改写一下,来感受下威力。

*public static boolean isSameProvince2(User user1, User user2) {
        return Optional.ofNullable(getUserConsigneeProvince(user1))
            .map(p -> Optional.ofNullable(getUserConsigneeProvince(user2))
                .filter(p2 -> p.equals(p2)))  
            .isPresent();
}

为了可读性我们写了4行,就算如此我们也减少了一半的代码,而且逻辑上更连贯,没有打断我们的可恶的null了。

总结

如上可以看出Optional在使用上带给我们的变化,让我们可以摆脱以往的null,用更加健康的调用方式来编写。也增加代码的可读性,逻辑上一气呵成。希望大家在平常多多使用。尽快远离恼人的null。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Linux shell 多进程和文件队列处理任务

    用一个shell脚本遍历需要拉取的机器和需要拉取天数的日志(两者可配置),通过scp命令将应用服务器上的日志拉取到日志服务器上,然后压缩存盘。再将过期的日期删除

    Dylan Liu
  • 设计模式之责任链模式

    责任链模式(Chain of Responsibility Pattern)属于设计模式的行为型模式。责任链模式与多米诺骨牌有点类似,请求在链中从前向后传递,一...

    Dylan Liu
  • Linux shell function

    在shell中创建的variable默认都全局变量,在函数中修改后会影响到variable的值,在函数中的变量前添加 local 关键字可以避免修改主程...

    Dylan Liu
  • 3分钟短文 | PHP判断null,别再 == 了,你真控制不住

    PHP 程序中很多地方会用到判断是否为空,比如字符串为空,数组为空,对象为空,或者其他数据类型为默认空值。

    程序员小助手
  • Java中有关Null的9件事

    对于Java程序员来说,null是令人头痛的东西。时常会受到空指针异常(NPE)的骚扰。连Java的发明者都承认这是他的一项巨大失误。Java为 什么要保留nu...

    哲洛不闹
  • Hive创建外部表CSV数据中列含有逗号问题处理

    在不能修改示例数据的结构情况下,这里需要使用Hive提供的Serde,在Hive1.1版本中提供了多种Serde,此处的数据通过属于CSV格式,所以这里使用默认...

    Fayson
  • JTable常见用法细则+设置某列可编辑+滚动表格

    JTable常见用法细则 JTable是Swing编程中很常用的控件,这里总结了一些常用方法以备查阅.欢迎补充,转载请注明作者与出处. 一. 创建表...

    YGingko
  • Java中有关Null的9件事

    对于Java程序员来说,null是令人头痛的东西。时常会受到空指针异常(NPE)的骚扰。连Java的发明者都承认这是他的一项巨大失误。Java为什么要保留nul...

    哲洛不闹
  • SQL,想说爱你并不是太容易的事

    不少人留言和留消息,只有一位差不多触及到了问题在哪里。公布一下答案之前先把题目贴一下。 ? ? ? 这道题目里面的坑主要在null这个东西。我们都知道SQL是基...

    用户1564362
  • Vue + Element UI 实现权限管理系统 前端篇(九)

    前台显示需要后台数据,我们这里先把前后端交互接口定义好,没有后台的时候,也方便用mock模拟。

    朝雨忆轻尘

扫码关注云+社区

领取腾讯云代金券