Java bean 链式获取成员变量无需判空的工具设计

本篇文章已发布至公众号 Hollis

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

那么,有什么办法可以避免在代码中写大量的判空语句呢?

有人说可以使用 JDK8提供的 Optional 来避免判空,但在嵌套比较深的对象中,需要不断地判空,用起来还是有些麻烦。

本篇博文设计了一种可以链式调用对象成员而无需判空的工具,相比原有的if null逻辑 和 JDK8提供的 Optional 更加优雅易用,在工程实践中大大提高了编码效率,也让代码更加的精准和优雅。

不优雅的判空调用

我想从事Java开发的小伙伴肯定有遇到过下面这种让人难受的判空逻辑:

现在有一个User类,School 是它的成员变量

  1. /**
  2. * @author Axin
  3. * @since 2020-09-20
  4. * @summary 一个User类定义
  5. * (Ps:Data 是lombok组件提供的注解,简化了get set等等的约定代码)
  6. */
  7. @Data
  8. public class User {
  9. private String name;
  10. private String gender;
  11. private School school;
  12. @Data
  13. public static class School {
  14. private String scName;
  15. private String adress;
  16. }
  17. }

现在想要获得School的成员变量 adress , 一般的处理方式:

  1. public static void main(String[] args) {
  2. User axin = new User();
  3. User.School school = new User.School();
  4. axin.setName("hello");
  5. if (Objects.nonNull(axin) && Objects.nonNull(axin.getSchool())) {
  6. User.School userSc = axin.getSchool();
  7. System.out.println(userSc.getAdress());
  8. }
  9. }

获取adress时要对School进行判空,虽然有些麻烦,到也能用,通过 JDK8 提供的 Optional 工具也是可以,但还是有些麻烦。

而下文的 OptionalBean 提供一种可以链式不断地调用成员变量而无需判空的方法,直接链式调用到你想要获取的目标变量,而无需担心空指针的问题。

链式调用成员变量

如果用了本文设计的工具 OptionalBean ,那么上述的调用可以简化成这样:

  1. public static void main(String[] args) {
  2. User axin = new User();
  3. User.School school = new User.School();
  4. axin.setName("hello");
  5. // 1. 基本调用
  6. String value1 = OptionalBean.ofNullable(axin)
  7. .getBean(User::getSchool)
  8. .getBean(User.School::getAdress).get();
  9. System.out.println(value1);
  10. }

执行结果:

其中User的school变量为空,可以看到代码并没有空指针,而是返回了null。这个工具怎么实现的呢?

OptionalBean 工具

  1. /**
  2. * @author Axin
  3. * @since 2020-09-10
  4. * @summary 链式调用 bean 中 value 的方法
  5. */
  6. public final class OptionalBean<T> {
  7. private static final OptionalBean<?> EMPTY = new OptionalBean<>();
  8. private final T value;
  9. private OptionalBean() {
  10. this.value = null;
  11. }
  12. /**
  13. * 空值会抛出空指针
  14. * @param value
  15. */
  16. private OptionalBean(T value) {
  17. this.value = Objects.requireNonNull(value);
  18. }
  19. /**
  20. * 包装一个不能为空的 bean
  21. * @param value
  22. * @param <T>
  23. * @return
  24. */
  25. public static <T> OptionalBean<T> of(T value) {
  26. return new OptionalBean<>(value);
  27. }
  28. /**
  29. * 包装一个可能为空的 bean
  30. * @param value
  31. * @param <T>
  32. * @return
  33. */
  34. public static <T> OptionalBean<T> ofNullable(T value) {
  35. return value == null ? empty() : of(value);
  36. }
  37. /**
  38. * 取出具体的值
  39. * @param fn
  40. * @param <R>
  41. * @return
  42. */
  43. public T get() {
  44. return Objects.isNull(value) ? null : value;
  45. }
  46. /**
  47. * 取出一个可能为空的对象
  48. * @param fn
  49. * @param <R>
  50. * @return
  51. */
  52. public <R> OptionalBean<R> getBean(Function<? super T, ? extends R> fn) {
  53. return Objects.isNull(value) ? OptionalBean.empty() : OptionalBean.ofNullable(fn.apply(value));
  54. }
  55. /**
  56. * 如果目标值为空 获取一个默认值
  57. * @param other
  58. * @return
  59. */
  60. public T orElse(T other) {
  61. return value != null ? value : other;
  62. }
  63. /**
  64. * 如果目标值为空 通过lambda表达式获取一个值
  65. * @param other
  66. * @return
  67. */
  68. public T orElseGet(Supplier<? extends T> other) {
  69. return value != null ? value : other.get();
  70. }
  71. /**
  72. * 如果目标值为空 抛出一个异常
  73. * @param exceptionSupplier
  74. * @param <X>
  75. * @return
  76. * @throws X
  77. */
  78. public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
  79. if (value != null) {
  80. return value;
  81. } else {
  82. throw exceptionSupplier.get();
  83. }
  84. }
  85. public boolean isPresent() {
  86. return value != null;
  87. }
  88. public void ifPresent(Consumer<? super T> consumer) {
  89. if (value != null)
  90. consumer.accept(value);
  91. }
  92. @Override
  93. public int hashCode() {
  94. return Objects.hashCode(value);
  95. }
  96. /**
  97. * 空值常量
  98. * @param <T>
  99. * @return
  100. */
  101. public static<T> OptionalBean<T> empty() {
  102. @SuppressWarnings("unchecked")
  103. OptionalBean<T> none = (OptionalBean<T>) EMPTY;
  104. return none;
  105. }
  106. }

工具设计主要参考了 Optional 的实现,再加上对链式调用的扩展就是上述的OptionalBean。

getBean 其实是当变量为空时返回了一个 包装空值的 OptionalBean 对象,同时泛型的使用让工具更加易用。

使用手册

可以看到代码中也提供了和 Optional 一样的扩展方法,如 ifPresent()、orElse()等等:

  1. public static void main(String[] args) {
  2. User axin = new User();
  3. User.School school = new User.School();
  4. axin.setName("hello");
  5. // 1. 基本调用
  6. String value1 = OptionalBean.ofNullable(axin)
  7. .getBean(User::getSchool)
  8. .getBean(User.School::getAdress).get();
  9. System.out.println(value1);
  10. // 2. 扩展的 isPresent方法 用法与 Optional 一样
  11. boolean present = OptionalBean.ofNullable(axin)
  12. .getBean(User::getSchool)
  13. .getBean(User.School::getAdress).isPresent();
  14. System.out.println(present);
  15. // 3. 扩展的 ifPresent 方法
  16. OptionalBean.ofNullable(axin)
  17. .getBean(User::getSchool)
  18. .getBean(User.School::getAdress)
  19. .ifPresent(adress -> System.out.println(String.format("地址存在:%s", adress)));
  20. // 4. 扩展的 orElse
  21. String value2 = OptionalBean.ofNullable(axin)
  22. .getBean(User::getSchool)
  23. .getBean(User.School::getAdress).orElse("家里蹲");
  24. System.out.println(value2);
  25. // 5. 扩展的 orElseThrow
  26. try {
  27. String value3 = OptionalBean.ofNullable(axin)
  28. .getBean(User::getSchool)
  29. .getBean(User.School::getAdress).orElseThrow(() -> new RuntimeException("空指针了"));
  30. } catch (Exception e) {
  31. System.out.println(e.getMessage());
  32. }
  33. }

run一下:

总结

设计了一种可以链式调用对象成员而无需判空的工具让代码更加的精准和优雅,如果本文设计的工具满足了刚好解决你的困扰,那就在项目中使用吧!

博主个人水平有限,如果有更的设计或者文中有错误,还请留言一起讨论,互相进步!

Java bean 链式获取成员变量无需判空的工具设计的更多相关文章

  1. Java使用反射来获取成员变量泛型信息

    Java通过指定类对应的Class对象,程序可以获得该类里包括的所有Field,不管该Field使用private修饰,还是使用public修饰.获得了Field对象后,就可以很容易的获得该Field ...

  2. Android(java)学习笔记109:通过反射获取成员变量和成员方法并且使用

    一.反射获取成员变量并且使用: 1.获取字节码文件对象:         Class c = Class.forName("cn.itcast_01.Person"); 2.使用无 ...

  3. java反射--获取成员变量信息

    获取成员变量信息 代码及说明: public static void printFieldMessage(Object obj) { //要获取类的信息,首先要获取类的类类型 Class c=obj. ...

  4. Android(java)学习笔记50:通过反射获取成员变量和成员方法并且使用

    1. 反射获取成员变量并且使用: (1)获取字节码文件对象:         Class c = Class.forName("cn.itcast_01.Person"); (2) ...

  5. Java反射理解(四)-- 获取成员变量构造函数信息

    Java反射理解(四)-- 获取成员变量构造函数信息 步骤 获取成员变量信息: obj.getClass() 获取类类型对象 成员变量也是对象,java.lang.reflect.Field 类中封装 ...

  6. java 27 - 4 反射之 通过反射获取成员变量并使用

    类Field: 提供有关类或接口的单个字段的信息,以及对它的动态访问权限. A:获得类的成员变量 数组: 1.getFields(公共类的) 2.getDeclaredFields(所有类型的) B: ...

  7. Java实现链式存储的二叉查找树(递归方法)

    二叉查找树的定义: 二叉查找树或者是一颗空树,或者是一颗具有以下特性的非空二叉树: 1. 若左子树非空,则左子树上所有节点关键字值均小于根节点的关键字: 2. 若右子树非空,则右子树上所有节点关键字值 ...

  8. Java实现链式存储的二叉树

    二叉树的定义: 二叉树(BinaryTree)是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的.分别称作这个根的左子树和右子树的二叉树组成. 二叉树的遍历方式主要 ...

  9. java实现链式队列

    java实现链式队列...比较简单 package datastruct; public class QueueLink implements Queue { // 定义一个节点内部类 class N ...

随机推荐

  1. Sunday算法解决字符串匹配问题

    概述 提起字符串匹配可能更多人会想到KMP算法,该算法时间复杂度为O(m+n),而且也是我们在学习数据结构过程中最早接触到的比较好的算法.但KMP算法需要在模式字符串有关联的情况下,也即模式字符串前后 ...

  2. leetcode刷题-51N皇后

    题目 n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击. 给定一个整数 n,返回所有不同的 n 皇后问题的解决方案. 每一种解法包含一个明确的 n 皇后问 ...

  3. opentracting+jager分布式链路追踪探索实践

    一.Opentracing opentracing通过提供平台无关.厂商无关的API,使得开发人员可以方便地实现追踪系统.opentracing提供了用于运营支撑系统和针对特定平台的辅助程序库,被跟踪 ...

  4. SpringMVC-结果跳转方式

    结果跳转方式 目录 结果跳转方式 1. ModelAndView 2. ServletAPI 3. SpringMVC实现 1. 无需视图解析器 2. 使用视图解析器 1. ModelAndView ...

  5. boostrap栅格系统自适应的布局

    1.栅格系统 ​ Bootstrap是基于移动优先的原则开发的,使用了一系列的媒体查询(media queries)方法,为我们的布局和界面创建自适应的的分界点.这些分界点主要是基于视口宽度的最小值, ...

  6. 2020年 .NET ORM 完整比较、助力选择

    .NET ORM 前言 为什么要写这篇文章? 希望针对 SEO 优化搜索引擎,让更多中国人知道并且使用.目前百度搜索 .NET ORM 全是 sqlsugar,我个人是无语的,每每一个人进群第一件事就 ...

  7. golang开发:CSP-WaitGroup Mutex

    CSP 是 Communicating Sequential Process 的简称,中文可以叫做通信顺序进程,是一种并发编程模型,最初于Tony Hoare的1977年的论文中被描述,影响了许多编程 ...

  8. pytest(2):使用pycharm运行pytest

    pycharm运行 1.在pycharm里创建测试文件test_demo.py # Author xuejie zeng # encoding utf-8 # content of test_demo ...

  9. 1. Linux系统优化

    1. 系统安装 本着纯净系统的原则,我们在安装系统时,应选择minimal install选项,来执行最小化安装,以便有需要时,安装我们需要的安装软件包. 操作系统为CentOS7.3 1611 下载 ...

  10. maven-shade-plugin插件未生效原因分析

    今天在项目的pom文件中引入maven-shade-plugin插件,构建一个uber-jar(包含所有依赖的jar包),但是诡异的事情出现了,执行mvn package后生成的jar包竟然没有包含被 ...