0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

JAVA8提供了Optional类来优化这种写法

Linux爱好者 来源:CSDN 作者:zjhred 2022-04-24 15:18 次阅读

引言

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

9b4c56a8-c39e-11ec-bce3-dac502259ad0.png

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

user.getAddress().getProvince();

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

if(user!=null){
Addressaddress=user.getAddress();
if(address!=null){
Stringprovince=address.getProvince();
}
}

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

一个连载多年还在继续更新的免费教程:http://blog.didispace.com/spring-boot-learning-2x/

API介绍

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

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

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

先说明一下,Optional(T value),即构造函数,它是private权限的,不能由外部调用的。其余三个函数是public权限,供我们所调用。那么,Optional的本质,就是内部储存了一个真实的值,在构造的时候,就直接判断其值是否为空。好吧,这么说还是比较抽象。直接上Optional(T value)构造函数的源码,如下图所示

9b5ee322-c39e-11ec-bce3-dac502259ad0.png

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

publicstaticOptionalof(Tvalue){
returnnewOptional<>(value);
}

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

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

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

publicfinalclassOptional<T>{
//省略....
privatestaticfinalOptionalEMPTY=newOptional<>();
privateOptional(){
this.value=null;
}
//省略...
publicstaticOptionalempty(){
@SuppressWarnings("unchecked")
Optionalt=(Optional)EMPTY;
returnt;
}
}

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

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

publicstaticOptionalofNullable(Tvalue){
returnvalue==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 other)和orElseThrow(Supplier exceptionSupplier)

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

@Test
publicvoidtest(){
Useruser=null;
user=Optional.ofNullable(user).orElse(createUser());
user=Optional.ofNullable(user).orElseGet(()->createUser());

}
publicUsercreateUser(){
Useruser=newUser();
user.setName("zhangsan");
returnuser;
}

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

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

Useruser=null;
Optional.ofNullable(user).orElseThrow(()->newException("用户不存在"));

3、map(Function mapper)和flatMap(Function> mapper)

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

直接上源码

publicfinalclassOptional<T>{
//省略....
publicOptionalmap(FunctionsuperT,?extendsU>mapper){
Objects.requireNonNull(mapper);
if(!isPresent())
returnempty();
else{
returnOptional.ofNullable(mapper.apply(value));
}
}
//省略...
publicOptionalflatMap(FunctionsuperT,Optional>mapper){
Objects.requireNonNull(mapper);
if(!isPresent())
returnempty();
else{
returnObjects.requireNonNull(mapper.apply(value));
}
}
}

这两个函数,在函数体上没什么区别。唯一区别的就是入参,map函数所接受的入参类型为Function,而flapMap的入参类型为Function>

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

如果User结构是下面这样的

publicclassUser{
privateStringname;
publicStringgetName(){
returnname;
}
}

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

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

对于flatMap而言:

如果User结构是下面这样的

publicclassUser{
privateStringname;
publicOptionalgetName(){
returnOptional.ofNullable(name);
}
}

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

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

4、isPresent()和ifPresent(Consumer consumer)

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

publicfinalclassOptional<T>{
//省略....
publicbooleanisPresent(){
returnvalue!=null;
}
//省略...
publicvoidifPresent(ConsumersuperT>consumer){
if(value!=null)
consumer.accept(value);
}
}

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

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

给写成

Useruser=Optional.ofNullable(user);
if(Optional.isPresent()){
//TODO:dosomething
}

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

至于ifPresent(Consumer consumer),用法也很简单,如下所示

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

5、filter(Predicate predicate)

不多说,直接上源码

publicfinalclassOptional<T>{
//省略....
Objects.requireNonNull(predicate);
if(!isPresent())
returnthis;
else
returnpredicate.test(value)?this:empty();
}

filter 方法接受一个 Predicate 来对 Optional 中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个 Optional;否则返回 Optional.empty

用法如下

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

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

一个连载多年还在继续更新的免费教程:http://blog.didispace.com/spring-boot-learning-2x/

实战使用

例一

在函数方法中

以前写法

publicStringgetCity(Useruser)throwsException{
if(user!=null){
if(user.getAddress()!=null){
Addressaddress=user.getAddress();
if(address.getCity()!=null){
returnaddress.getCity();
}
}
}
thrownewExcpetion("取值错误");
}

JAVA8写法

publicStringgetCity(Useruser)throwsException{
returnOptional.ofNullable(user)
.map(u->u.getAddress())
.map(a->a.getCity())
.orElseThrow(()->newException("取指错误"));
}

例二

比如,在主程序中

以前写法

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

JAVA8写法

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

例三

以前写法

publicUsergetUser(Useruser)throwsException{
if(user!=null){
Stringname=user.getName();
if("zhangsan".equals(name)){
returnuser;
}
}else{
user=newUser();
user.setName("zhangsan");
returnuser;
}
}

java8写法

publicUsergetUser(Useruser){
returnOptional.ofNullable(user)
.filter(u->"zhangsan".equals(u.getName()))
.orElseGet(()->{
Useruser1=newUser();
user1.setName("zhangsan");
returnuser1;
});
}

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

审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • JAVA
    +关注

    关注

    19

    文章

    2904

    浏览量

    102989
  • API
    API
    +关注

    关注

    2

    文章

    1381

    浏览量

    60988

原文标题:Java8 判空新写法!

文章出处:【微信号:LinuxHub,微信公众号:Linux爱好者】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Android编译优化之混淆配置

    为了使用java8及后续java新版本的特性,Google增加了一步编译过程—脱糖(desugaring),但这一步会导致更长的编译时间,这也是为什么Google会推出D8和R8编译器来优化编译速度。
    的头像 发表于 12-21 09:21 452次阅读
    Android编译<b class='flag-5'>优化</b>之混淆配置

    Java提供了哪些注释语句

    Java提供了很多种类型的注释语句,它们都有不同的用途和作用。在本文中,我们将详细介绍Java中的注释语句类型,并讨论它们的用法和如何正确使用它们。 单行注释(Single-line
    的头像 发表于 11-28 16:56 363次阅读

    insert into 语句的三种写法

    INSERT INTO是MySQL中常用的一种SQL语句,用于将数据插入到表中。此文将详细介绍INSERT INTO语句的三种不同写法及其用途,并提供代码示例和相关解释。 正文: 一、基本插入
    的头像 发表于 11-21 14:18 2799次阅读

    insertinto语句的三种写法

    写法,它们分别是: 省略列名写法: INSERT INTO table_name VALUES (value1, value2, ...); 这种写法是最简单的方式,它忽略了列名,直
    的头像 发表于 11-17 15:12 1199次阅读

    为什么需要Streams?它们能取代Java中的for循环吗?

    Java8的发布是Java历史上的一个重大时刻。Streams 和 Lambda 被引入,它们现在被广泛使用。
    的头像 发表于 11-03 09:39 248次阅读
    为什么需要Streams?它们能取代<b class='flag-5'>Java</b>中的for循环吗?

    串口发送的这几种写法,你用过几种?

    串口发送的这几种写法,你用过几种?
    的头像 发表于 11-02 16:20 707次阅读
    串口发送的这几种<b class='flag-5'>写法</b>,你用过几种?

    Java8的新特性

    虽然目前Java最新版本都已经到16了,但是绝大部分公司目前用的Java版本都是8,想当初Java8问世后,其Lambda表达式与方法引用可是最亮眼的新特性,目前,这两个特性也被大家广泛使用,所以
    的头像 发表于 10-10 17:12 300次阅读

    JDK8新增的Optional类的常用方法

    解决空指针异常的一种方式,不赞成写过多的代码来显式检查 null ,以期望程序员写出整洁同时可读性更高的代码。 受 Google Guava 的影响,Optional 现在也成为了Java 8 及以上
    的头像 发表于 09-30 15:13 271次阅读

    如何通过注解来优化我们的Java代码

    Java注解可以说是我们编码过程中最常用的。本篇文章将给大家介绍Java注解的概念、作用以及如何使用注解来提升代码的可读性和灵活性,并介绍如何通过注解来优化我们的Java代码。 1、什
    的头像 发表于 09-30 11:39 326次阅读

    Java8的Stream流 map() 方法

    前言 在日常的开发工作中经常碰到要处理 List 中数据的问题,比如从一个对象集合中获得对象中的一个属性的集合。之前我们想到的是遍历每个元素,然后取出来放到另外一个集合中,比较繁琐;在 Java8
    的头像 发表于 09-25 11:06 714次阅读
    <b class='flag-5'>Java8</b>的Stream流 map() 方法

    JDK 21 GA,虚拟线程正式稳定!你还坚守Java8

    Java 21 / JDK 21 已正式 GA,此版本是继 JDK 17 后的长期支持版本 (LTS),Oracle 将为其提供至少八年的技术支持和更新。
    的头像 发表于 09-20 15:47 647次阅读
    JDK 21 GA,虚拟线程正式稳定!你还坚守<b class='flag-5'>Java8</b>?

    怎么使用Java8的Stream API比较两个List的差异呢?

    可以使用Java8的Stream API来比较两个List的差异,并取出不同的对象。
    的头像 发表于 08-12 11:15 1578次阅读

    C语言main函数的正确写法

    大家好,我是嵌入式老林,从事嵌入式软件开发多年,今天分享的内容是C语言main函数的正确写法,希望能对你有所帮助
    发表于 07-11 11:51 435次阅读
    C语言main函数的正确<b class='flag-5'>写法</b>

    优化指南:释放Java更高性能

    在过去的十年中,Java 已经成为最流行的云编程语言之一。Hadoop、Cassandra 和 Kafka 等流行的云应用程序都使用 Java 语言和框架。Java 是一种通用的面向对象语言
    的头像 发表于 06-29 18:19 646次阅读
    <b class='flag-5'>优化</b>指南:释放<b class='flag-5'>Java</b>更高性能

    Java web应用程序与esp8266通信以进行硬件控制,怎么实现?

    ,Netbeans IDE 用于 java 代码;另一方面,我有一张带有 8 位 PIC 微控制器 + esp8266 模块 + 和直流电机的电子卡控制开门;应用程序密钥将是java
    发表于 05-22 06:18