首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

瞧瞧别人家的判空,那叫一个优雅!

在开发过程中,最令人头疼的错误之一莫过于空指针异常(NPE)了。作为程序员,谁没因为一个突如其来的NullPointerException而懊恼过?

想想看,项目上线前,整个团队都在忙着部署,突然来了个堆栈错误,老半天搞不明白是哪个变量的值没初始化。这一看代码,满屏的if (xxx == null),复杂到让人头晕。别提多低效、低级了。

但是,我要说,判空的方式有很多种,大家一定要试试“别人家的代码”,那叫一个优雅,简直是代码的艺术品。

接下来,我们一起来探讨一下 Java 中如何优雅、高效地进行判空操作,从而避免空指针异常,提高代码的可读性和可维护性。

一、传统判空的血泪史

在我们之前的开发过程中,最常见的判空方式,可能就是这样了:

if (user != null) {

  if (user.getAddress() != null) {

      if (user.getAddress().getStreet() != null) {

          // 做一些事情

      }

  }

}

一层一层的嵌套判断,看着让人崩溃,尤其是当这种判空出现在多个地方时,整个代码的可读性极差,简直是灾难级别的存在。再加上一些对外部数据接口的调用,一不小心就会产生大量空指针异常,尤其是在高并发的情况下,程序崩溃的概率简直成倍增长。

二、Java 8+ 时代的判空革命

随着 Java 8 的到来,我们迎来了判空操作的一次革命——Optional!这个神器让判空操作变得优雅、简洁、链式调用,程序员们都爱不释手。

1. Optional 黄金三板斧

链式调用判空:使用Optional.ofNullable()

Optional是一个容器对象,它能帮助我们避免空指针异常。通过Optional.ofNullable(),我们可以优雅地处理可能为null的对象,而无需一层层地嵌套判断。让我们看个例子:

Optional.ofNullable(user)

      .map(User::getAddress)

      .map(Address::getStreet)

      .ifPresent(street -> {

          // 在这里使用 street,避免了 NullPointerException

      });

看!这段代码比那段嵌套的if优雅多了吧?简洁明了,能够有效避免NullPointerException,而且还能链式调用,代码的可读性大大提升。

高级用法:条件过滤与业务异常抛出

Optional还能用于更复杂的情况,比如在判空时抛出业务异常。比如我们有一个方法,接收一个可能为空的用户对象,并希望如果用户没有地址,就抛出一个自定义的异常:

User user = getUser();

String street = Optional.ofNullable(user)

      .map(User::getAddress)

      .map(Address::getStreet)

      .orElseThrow(() -> new IllegalStateException("用户地址不能为空"));

当user或者address为null时,代码会自动抛出IllegalStateException,避免了不必要的 null 检查。

2. 封装通用工具类

你可能会想,写了这么多判空代码,我能不能封装一个工具类,让其他地方直接调用?当然可以。你可以封装一个NullSafe工具类,让判空变得更加简单。比如:

public class NullSafe {

  public static <T> Optional<T> ofNullable(T value) {

      return Optional.ofNullable(value);

  }

}

然后直接调用:

String street = NullSafe.ofNullable(user)

      .map(User::getAddress)

      .map(Address::getStreet)

      .orElse("默认街道");

三、现代化框架的判空银弹

1. Spring 实战技巧

如果你在用 Spring 框架,那么 Spring 提供了一些非常好用的工具类来帮助我们判空,比如CollectionUtils和StringUtils。

if (CollectionUtils.isEmpty(userList)) {

  // 处理用户列表为空的情况

}

if (StringUtils.isEmpty(user.getName())) {

  // 处理用户名为空的情况

}

这些工具类能大大简化我们的代码,让判空更加直接和简洁。

2. Lombok 保驾护航

Lombok 是我们项目中的老朋友了,@NonNull注解简直是自动生成判空代码的神奇宝贝。通过它,我们可以在方法参数中标记某个参数不能为null,如果为null,Lombok 会自动抛出NullPointerException,省去了手动检查的麻烦。

public void setUserName(@NonNull String name) {

  this.name = name;

}

调用这个方法时,如果传入null,Lombok 会帮我们自动抛出NullPointerException,这样一来,代码简洁且异常处理得当。

四、工程级解决方案

在一些大型项目中,空指针检查可能变得尤为复杂,传统的判空方式已经不适用了,这时我们可以考虑采用一些工程级的方案。

1. 空对象模式

空对象模式是一种很巧妙的解决方案,它通过定义一个空对象来替代null,避免了频繁的 null 检查。比如,我们可以定义一个Notification接口的空对象,实现一个空的Notification对象,从而避免频繁的 null 检查。

public class EmptyNotification implements Notification {

  @Override

  public void notifyUser() {

      // 什么也不做

  }

}

这样,当没有通知需要发送时,直接使用EmptyNotification,不需要担心null的问题。

2. Guava 的 Optional 增强

Guava 的Optional提供了一些更为强大的功能,比如transform和or方法,能够帮助我们进行更复杂的操作。比如:

Optional<String> name = Optional.of("John");

String upperName = name.transform(String::toUpperCase).or("Default Name");

这段代码用 Guava 的Optional实现了String大写转换,并且提供了一个默认值,简洁且优雅。

五、防御式编程进阶

在一些关键性业务中,我们还可以通过防御式编程来增强系统的健壮性,防止出现 null 值导致的问题。

1. Assert 断言式拦截

断言(assert)能够帮助我们验证某些关键参数在方法执行之前是有效的。如果某个参数为null,程序会立即抛出异常。

public void processData(@NonNull String data) {

  assert data != null : "数据不能为空";

  // 处理数据

}

这样我们就能确保传入的数据不会为null,提高了代码的健壮性。

2. 全局 AOP 拦截

AOP 拦截可以帮助我们全局处理参数判空的逻辑,尤其是在接口调用时非常有用。通过自定义注解与 AOP 结合,我们可以在调用接口之前拦截请求,进行判空处理,避免了重复编写判空代码。

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface NotNullCheck {

  // 自定义判空注解

}

然后通过 AOP 拦截:

@Aspect

@Component

public class NullCheckAspect {

  @Before("@annotation(NotNullCheck)")

  public void checkParamsNotNull(JoinPoint joinPoint) {

      for (Object arg : joinPoint.getArgs()) {

          if (arg == null) {

              throw new IllegalArgumentException("参数不能为null");

          }

      }

  }

}

六、总结

Java 中的判空问题,从传统的多层if判空到 Java 8 引入的Optional,再到现代化框架的帮助,已经有了不少优雅的解决方案。程序员不再需要一遍遍地写冗长的if判断,代码也变得更简洁、可读性更高。

但是,正如代码中的每一行都能传递我们的设计思路一样,优雅的判空也需要我们在写代码时保持思考。毕竟,写出来的代码是给自己看的,是要长期维护的,不仅仅是让“别人家”的代码更美。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OUiSC8hL94l1NlZPUECzhz-g0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券