引言

在文章的开头,先说下NPE问题,NPE问题就是,我们在开发中经常碰到的NullPointerException.假设我们有两个类,他们的UML类图如下图所示

图片

在这种情况下,有如下代码

user.getAddress().getProvince();

这种写法,在user为null时,是有可能报NullPointerException异常的。为了解决这个问题,于是采用下面的写法

if(user!=null){  Address address = user.getAddress();  if(address!=null){  String province = address.getProvince();  }
}

这种写法是比较丑陋的,为了避免上述丑陋的写法,让丑陋的设计变得优雅。JAVA8提供了Optional类来优化这种写法,接下来的正文部分进行详细说明

API介绍

先介绍一下API,与其他文章不同的是,本文采取类比的方式来讲,同时结合源码。而不像其他文章一样,一个个API罗列出来,让人找不到重点。

1、Optional(T value),empty(),of(T value),ofNullable(T value)

这四个函数之间具有相关性,因此放在一组进行记忆。

先说明一下,Optional(T value),即构造函数,它是private权限的,不能由外部调用的。其余三个函数是public权限,供我们所调用。

那么,Optional的本质,就是内部储存了一个真实的值,在构造的时候,就直接判断其值是否为空。好吧,这么说还是比较抽象。直接上Optional(T value)构造函数的源码,如下图所示

图片

那么,of(T value)的源码如下

public static <T> Optional<T> of(T value) {  return new Optional<>(value);
}

也就是说of(T value)函数内部调用了构造函数。根据构造函数的源码我们可以得出两个结论:

  • 通过of(T value)函数所构造出的Optional对象,当Value值为空时,依然会报NullPointerException。
  • 通过of(T value)函数所构造出的Optional对象,当Value值不为空时,能正常构造Optional对象。

除此之外呢,Optional类内部还维护一个value为null的对象,大概就是长下面这样的

public final class Optional<T> {  //省略....  private static final Optional<?> EMPTY = new Optional<>();  private Optional() {  this.value = null;  }  //省略...  public static<T> Optional<T> empty() {  @SuppressWarnings("unchecked")  Optional<T> t = (Optional<T>) EMPTY;  return t;  }
}

那么,empty()的作用就是返回EMPTY对象。

好了铺垫了这么多,可以说ofNullable(T value)的作用了,上源码

public static <T> Optional<T> ofNullable(T value) {  return value == null ? empty() : of(value);
}

好吧,大家应该都看得懂什么意思了。相比较of(T value)的区别就是,当value值为null时,of(T value)会报NullPointerException异常;ofNullable(T value)不会throw Exception,ofNullable(T value)直接返回一个EMPTY对象。

那是不是意味着,我们在项目中只用ofNullable函数而不用of函数呢?

不是的,一个东西存在那么自然有存在的价值。当我们在运行过程中,不想隐藏NullPointerException。而是要立即报告,这种情况下就用Of函数。但是不得不承认,这样的场景真的很少。博主也仅在写junit测试用例中用到过此函数。

2、orElse(T other),orElseGet(Supplier<? extends T> other)和orElseThrow(Supplier<? extends X> exceptionSupplier)

这三个函数放一组进行记忆,都是在构造函数传入的value值为null时,进行调用的。orElse和orElseGet的用法如下所示,相当于value值为null时,给予一个默认值:

@Test
public void test() {  User user = null;  user = Optional.ofNullable(user).orElse(createUser());  user = Optional.ofNullable(user).orElseGet(() -> createUser());  }
public User createUser(){  User user = new User();  user.setName("zhangsan");  return user;
}

这两个函数的区别:当user值不为null时,orElse函数依然会执行createUser()方法,而orElseGet函数并不会执行createUser()方法,大家可自行测试。

至于orElseThrow,就是value值为null时,直接抛一个异常出去,用法如下所示

User user = null;
Optional.ofNullable(user).orElseThrow(()->new Exception("用户不存在"));

3、map(Function<? super T, ? extends U> mapper)和flatMap(Function<? super T, Optional<U>> mapper)

这两个函数放在一组记忆,这两个函数做的是转换值的操作。

直接上源码

 public final class Optional<T> {  //省略....  public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {  Objects.requireNonNull(mapper);  if (!isPresent())  return empty();  else {  return Optional.ofNullable(mapper.apply(value));  }  }  //省略...  public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {  Objects.requireNonNull(mapper);  if (!isPresent())  return empty();  else {  return Objects.requireNonNull(mapper.apply(value));  }  }
}

这两个函数,在函数体上没什么区别。唯一区别的就是入参,map函数所接受的入参类型为Function<? super T, ? extends U>,而flapMap的入参类型为Function<? super T, Optional<U>>。(搜索公众号Java知音,回复“2021”,送你一份Java面试题宝典)

在具体用法上,对于map而言:

如果User结构是下面这样的

public class User {  private String name;  public String getName() {  return name;  }
}

这时候取name的写法如下所示

String city = Optional.ofNullable(user).map(u-> u.getName()).get();

对于flatMap而言:

如果User结构是下面这样的

public class User {  private String name;  public Optional<String> getName() {  return Optional.ofNullable(name);  }
}

这时候取name的写法如下所示

String city = Optional.ofNullable(user).flatMap(u-> u.getName()).get();

4、isPresent()ifPresent(Consumer<? super T> consumer)

这两个函数放在一起记忆,isPresent即判断value值是否为空,而ifPresent就是在value值不为空时,做一些操作。这两个函数的源码如下

public final class Optional<T> {  //省略....  public boolean isPresent() {  return value != null;  }  //省略...  public void ifPresent(Consumer<? super T> consumer) {  if (value != null)  consumer.accept(value);  }
}

需要额外说明的是,大家千万不要把

if (user != null){  // TODO: do something
}

给写成

User user = Optional.ofNullable(user);
if (Optional.isPresent()){  // TODO: do something
}

因为这样写,代码结构依然丑陋。博主会在后面给出正确写法

至于ifPresent(Consumer<? super T> consumer),用法也很简单,如下所示

Optional.ofNullable(user).ifPresent(u->{  // TODO: do something
});

5、filter(Predicate<? super T> predicate)

不多说,直接上源码

public final class Optional<T> {  //省略....  Objects.requireNonNull(predicate);  if (!isPresent())  return this;  else  return predicate.test(value) ? this : empty();
}

filter 方法接受一个 Predicate 来对 Optional 中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个 Optional;否则返回 Optional.empty。(搜索公众号Java知音,回复“2021”,送你一份Java面试题宝典)

用法如下

Optional<User> user1 = Optional.ofNullable(user).filter(u -> u.getName().length()<6);

如上所示,如果user的name的长度是小于6的,则返回。如果是大于6的,则返回一个EMPTY对象。

实战使用

例一

在函数方法中

以前写法

public String getCity(User user)  throws Exception{  if(user!=null){  if(user.getAddress()!=null){  Address address = user.getAddress();  if(address.getCity()!=null){  return address.getCity();  }  }  }  throw new Excpetion("取值错误");   }

JAVA8写法

public String getCity(User user) throws Exception{  return Optional.ofNullable(user)  .map(u-> u.getAddress())  .map(a->a.getCity())  .orElseThrow(()->new Exception("取指错误"));
}

例二

比如,在主程序中

以前写法

if(user!=null){  dosomething(user);
}

JAVA8写法

Optional.ofNullable(user)  .ifPresent(u->{  dosomething(u);
});

例三

以前写法

public User getUser(User user) throws Exception{  if(user!=null){  String name = user.getName();  if("zhangsan".equals(name)){  return user;  }  }else{  user = new User();  user.setName("zhangsan");  return user;  }
}

java8写法

public User getUser(User user) {  return Optional.ofNullable(user)  .filter(u->"zhangsan".equals(u.getName()))  .orElseGet(()-> {  User user1 = new User();  user1.setName("zhangsan");  return user1;  });
}

采用这种链式编程,虽然代码优雅了。但是,逻辑性没那么明显,可读性有所降低,大家项目中看情况酌情使用。

【Java 8】教你用 Java 8中的 Optional 更优雅的判空相关推荐

  1. 在 Java 中如何优雅地判空

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 李良逸 来源 | blog.imuxuan.c ...

  2. 如何判断map为空_在Java中如何优雅地判空

    来自:李良逸 链接:http://blog.imuxuan.com/archives/86 判空灾难 作为搬砖党的一族们,我们对判空一定再熟悉不过了,不要跟我说你很少进行判空,除非你喜欢 NullPo ...

  3. java判断邮箱名和文件名_Java公开课|想学好Java,教你操作Java校验文件名和邮箱地址,快来看看...

    [摘要]作为一门面向对象编程语言,Java吸收了C++语言的优点,也展现了其强大的一面,我们能在各个地方看到其功能强大和简单易用的两个特征,当然,也吸引了很多程序员的注意力,所以就有人想了解Java的 ...

  4. 【Kotlin】Kotlin 单例 ( 懒汉式 与 恶汉式 | Java 单例 | Kotlin 单例 | 对象声明 | 伴生对象 | get 方法 | ? 与 !! 判空 )

    文章目录 I . 单例的懒汉式与恶汉式 II . Java 中的懒汉式与恶汉式 III . Kotlin 中对应 Java 的懒汉式与恶汉式 IV . Kotlin 对象 ( object ) 声明 ...

  5. Java集合优雅的判空/非空

    一.乱象 代码中各种同胞写的各种集合判空,很多,很杂乱.大多数是不规范的,而且可能会造成空指针异常. 这篇讲的CollectionUtils工具类是在apache下的, 而不是springframew ...

  6. java如何优雅的判空

    作为搬砖党的一族们,我们对判空一定再熟悉不过了,不要跟我说你很少进行判空,除非你喜欢 NullPointerException. 不过 NullPointerException 对于很多猿们来说,也是 ...

  7. c语言c2182是什么错误,C语言中一种更优雅的异常处理机制

    上一篇文章对C语言中的goto语句进行了较深入的阐述,实际上goto语句是面向过程与面向结构化程序语言中,进行异常处理编程的最原始的支持形式.后来为了更好地.更方便地支持异常处理编程机制,使得程序员在 ...

  8. Java中操作字符串的工具类-判空、截取、格式化、转换驼峰、转集合和list、是否包含

    场景 某些常用的对字符串进行处理的方法抽离出来成工具类,方便在多处应用. 常用的操作为: 判断是否为空 截取字符串 格式化文本 字符串转set 字符串转list 下划线转驼峰命名 是否包含字符串 注: ...

  9. java入门教学书,Java面试技巧合集

    一.内存与线程 1.内存结构 内存是计算机的重要部件之一,它是外存与CPU进行沟通的桥梁,计算机中所有程序的运行都在内存中进行,内存性能的强弱影响计算机整体发挥的水平.JVM的内存结构规定Java程序 ...

最新文章

  1. 中秋将至,联合几个号主送出价值500元的中秋大礼包
  2. R可视化使用ggplot2创建样本数据热力图(heatmap)
  3. 给终端装上宝可梦主题!小姐姐路过都爱上了!
  4. centos oracle命令,CentOS7命令行方式搭建Oracle11gR2
  5. 剑指offer:把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
  6. 有哪些足不出户,能用十天掌握的新技能?
  7. SAP Spartacus cost center list class的赋值逻辑
  8. html5在线音乐列表播放器,HTML5列表音乐播放器SMusic
  9. 用可编写脚本的终结点遍历REST应用程序
  10. 【Windows CMD】设置编码格式
  11. Jquery-数组删除元素
  12. 信号与系统—傅里叶级数
  13. CentOS7下载安装JDK1.8
  14. programer的自我修养
  15. maya动画镜像_Maya
  16. android问卷调查论文设计,基于 android问卷调查app开发与设计.pdf
  17. python读excel表_怎么用python读取excel表格的数据
  18. 在聊天群中,如何快速获取信息,了解群中讨论的话题。
  19. 武汉城建学院计算机专业柴曲,采访对话丨在世界一流的柴院学音乐,是怎样一种感受?...
  20. Word2019版_去掉文档中的回车符

热门文章

  1. 数据库优先生成EF CRUD演示
  2. php : 工厂类演示
  3. 极客先锋 如何生成git的公钥和私钥
  4. jQuery中 trigger() 使用心得
  5. 利用gp自己做的生成缓冲区的代码
  6. 没有调用save或update方法,却有sql语句执行
  7. 电力企业信息化系统主要报表展示
  8. Box2D——入门教程
  9. 一部分 数据 迁移_超原版速度110倍,针对PyTorch的CPU到GPU张量迁移工具开源
  10. python基本函数归整