实用的Optional使用技巧

4 篇文章 0 订阅
不管是Java新人还是Java高工,在开发过程中都会碰到需要判断Null值以防止空指针的情况,以往的方式要么是抛异常,要么是if{}else{},这种方式往往会造成代码的阅读性和维护性变差。这篇文章将围绕在Java8中推出的Optional<T>,来讨论如何优雅的解决判空问题。

1. 为什么使用Optional

主要用处:防止空指针(NPE)、简化if...else...判断、减少代码圈复杂度

代码示例:

// 根据userId获取用户信息
User user = userDao.getUser(userId);
// 需要对user判空
if (user != null) {
    // user不为null时,还需要对user的属性判断,做对应操作
    if (user.getName() != null) {
        System.out.println("用户姓名:" + user.getName());
    }
    if (user.getSex() != null) {
        System.out.println("用户性别:" + user.getSex());
    }
    if (user.getAge() != null) {
        System.out.println("用户年龄:" + user.getAge());
    }
    if (user.getAddress() != null) {
        System.out.println("用户地址:" + user.getAddress());
    }
    // ...还有其他属性判断
}

可以看到在获取到user后必须要进行非空判断,不然就会报空指针异常(NPE),同样下面的输出属性值也是一样,必须经过判断才能输出,这样就会出现大量的【!= null】逻辑判断,非常不优雅。

而Optional就解决了以上的问题,让使用者只关心业务逻辑,更加优雅的解决判空问题。

2. 什么是Optional

我们可以把Optional想象成一个盒子,而里面装的就是你需要判空的对象,这样你只需要判断盒子里是不是空的,就可以判断是不是空对象,而不需要判断盒子本身,因为盒子一直存在(盒子不可能为NULL),如下图:

Optional是Java 8引入的新特性,并在后续版本进行了优化,增添了更多新的功能,本文会标识常用方法可以使用的版本。

3. 常用方法举例

3.1 创建空的Optional:empty()

常见用法:业务不满足某条件时,返回空的Optional

public Optional<User> getManUser(Long userId) {
    // ...一些业务逻辑
    if (user.getSex() != Sex.MAN) {
        // 如果性别是男,直接返回空
        return Optional.empty();
    }
}

3.2 创建非空的Optional:of()

常见用法:组装一个非空的Optional便于后续业务处理

public Optional<User> getAdultUser(Long userId) {
    // ...一些业务逻辑
    if (user.getAge() >= 18) {
        // 如果成年了,则返回
        return Optional.of(user);
    }
}

3.3 创建需要判空的Optional:ofNullable()

常见用法:从数据库或接口获取一个对象后,不知道是不是空,需要进行判断

public Optional<User> getUserFromUserCenter(Long userId) {
    // ...一些业务逻辑
    // 从外部接口获取了User信息
    User user = userService.getUser(userId);
    Optional<User> userOpt = Optional.ofNullable(user);
    // 或者简化直接进行包装
    Optional<User> userOpt = Optional.ofNullable(userService.getUser(userId));
}

3.4 判断Optional中对象是否是NULL:isPresent()

常见用法:判空后进行业务处理

// 接3中例子
if (userOpt.isPresent()) {
    // 返回true,说明user不为NULL,则对其中的user对象进行处理
} else {
    // 返回false,说明user为NULL
    System.out.println("用户不存在");
}

3.5 非空表达式if:ifPresent()

常见用法:代替if(xxx != null){},判断非空之后进行逻辑处理

// 接3中例子
// 如果user存在,则打印出名字
userOpt.ifPresent(user -> System.out.println(user.getName()));

3.6 非空表达式if else(JAVA9) :ifPresentOrElse()

常见用法:代替if(xxx != null){} else {},对空和非空分别进行逻辑处理

// 如果user存在,则打印出名字, 否则提示不存在,相当于4中代码的改造
userOpt.ifPresentOrElse(user -> System.out.println(user.getName()), () -> System.out.println("用户不存在"));

3.7 设置默认值:orElse()

常见用法:当对象为NULL时,需要指定一个默认值

// 如果user为NULL,则给new一个User
User user = Optional.ofNullable(user).orElse(new User());

3.8 设置默认值:orElseGet()

常见用法:当对象为NULL时,需要指定一个默认值,与orElseGet()的区别是,如果Optional中对象不为Null,则不会执行orElseGet()中的方法

// 如果user为NULL,则从getDefaultUser中获取一个User
User user = Optional.ofNullable(user).orElseGet(UserUtil::getDefaultUser);

// 以下是orElse()和orElseGet()的对比
// 如果user为null,则两者都会执行UserUtil.getDefaultUser()方法
// 如果user不为null,则orElse会执行UserUtil.getDefaultUser()方法,orElseGet不会
// 所以某些情况下orElseGet性能更好
User user = Optional.ofNullable(user).orElse(UserUtil.getDefaultUser());
User user = Optional.ofNullable(user).orElseGet(UserUtil::getDefaultUser);

3.9 过滤值:filter()

常见用法:需要对Optional中的对象进行过滤

// 判断user的年龄是否大于18岁
Optional<User> userOpt = Optional.ofNullable(user);
System.out.println(userOpt.filter(user -> user.getAge() >= 18).isPresent());

3.10 转换值:map()

常见用法:将原始的Optional对象转换为新的Optional对象

// 将Optional<User>转换为Optional<Developer>
Optional<Developer> devOpt = Optional.ofNullable(user).map(user -> {
    Developer dev = net Developer();
    dev.setDevName(user.getName());
    return dev;
});

4. 实战改造

4.1 解决checkStyle问题

开发过程中经常会碰到这种问题,可以使用Optional简化

原代码:

BaseMasterSlaveServersConfig smssc = new BaseMasterSlaveServersConfig();
if (clientName != null) {
    smssc.setClientName(clientName);
}
if (idleConnectionTimeout != null) {
    smssc.setIdleConnectionTimeout(idleConnectionTimeout);
}
if (connectTimeout != null) {
    smssc.setConnectTimeout(connectTimeout);
}
if (timeout != null) {
    smssc.setTimeout(timeout);
}
if (retryAttempts != null) {
    smssc.setRetryAttempts(retryAttempts);
}
if (retryInterval != null) {
    smssc.setRetryInterval(retryInterval);
}
if (reconnectionTimeout != null) {
    smssc.setReconnectionTimeout(reconnectionTimeout);
}
if (password != null) {
    smssc.setPassword(password);
}
if (failedAttempts != null) {
    smssc.setFailedAttempts(failedAttempts);
}
// ...后面还有很多这种判断,一个if就是一个分支,会增长圈复杂度

改造后:

Optional.ofNullable(clientName).ifPresent(smssc::setClientName);
Optional.ofNullable(idleConnectionTimeout).ifPresent(smssc::setIdleConnectionTimeout);
Optional.ofNullable(connectTimeout).ifPresent(smssc::setConnectTimeout);
Optional.ofNullable(timeout).ifPresent(smssc::setTimeout);
Optional.ofNullable(retryAttempts).ifPresent(smssc::setRetryAttempts);
Optional.ofNullable(retryInterval).ifPresent(smssc::setRetryInterval);
Optional.ofNullable(reconnectionTimeout).ifPresent(smssc::setReconnectionTimeout);
// ...缩减为一行,不但减少了圈复杂度,而且减少了行数

4.2 简化if...else...

原代码:

// 获取用户,找不到则新增,找到则改变用户状态
User user = userDao.getUserById(userId);
if (user == null) {
    User user = new User();
    user.setName("用户");
    userDao.save(user);
} else {
    user.setStatus(1);
    userDao.update(user);
}

改造后:

// 把新增用户和更新用户的方法提出来,可读性更高
Optional.ofNullable(userDao.getUserById(userId))
        .ifPresentOrElse(user -> saveUser(), user -> updateUser(user));

// 保存用户
private void saveUser(){
    User user = new User();
    user.setName("用户");
    userDao.save(user);
}

// 更新用户
private void updateUser(User user){
    user.setStatus(1);
    userDao.update(user);
}

5. 本文作者及团队介绍

王翰卿,曾任快手商业化高级Java开发,现三翼鸟交付中心Java开发工程师,坚信优雅的开发方式是提升个人技术水平的的关键,业余爱好钢琴,主机游戏,来自三翼鸟数字化技术平台-交易交付能力平台团队。

交易交付能力平台团队负责搭建门店数字化转型工具,包括:海尔智家体验店小程序、三翼鸟工作台APP、商家中心等产品形态,通过数字化工具,实现门店的用户上平台、交互上平台、交易上平台、交付上平台,从而助力海尔专卖店的零售转型,并实现三翼鸟店的场景创新。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值