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

    文章

    2971

    浏览量

    104854
  • API
    API
    +关注

    关注

    2

    文章

    1504

    浏览量

    62163

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

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

收藏 人收藏

    评论

    相关推荐

    Java集合API的改进介绍

    解答这些问题。 我们将逐步学习 Java 集合优化过程,并按版本逐一对比分析。主要讨论的焦点将包括 JDK 1.0、1.2、1.4、1.5、1.6、1.8、9、10、11 和 21 版本的
    的头像 发表于 11-22 11:12 220次阅读
    <b class='flag-5'>Java</b>集合API的改进介绍

    Java 枚举与策略模式、函数式接口的结合:实现高内聚低耦合的设计

    作者:京东物流 杨唯一 一、Java 枚举 Java 枚举是一个特殊的,一般表示一组常量,比如一年的 4 个季节,一年的 12 个月份,一个星期的 7 天,方向有东南西北等。 我们
    的头像 发表于 11-21 14:06 239次阅读

    使用Arthas火焰图工具的Java应用性能分析和优化经验

    分享作者在使用Arthas火焰图工具进行Java应用性能分析和优化的经验。
    的头像 发表于 10-28 09:27 300次阅读
    使用Arthas火焰图工具的<b class='flag-5'>Java</b>应用性能分析和<b class='flag-5'>优化</b>经验

    优化汽车48V电池和电动自行车应用中的线路电压和负载调节

    电子发烧友网站提供优化汽车48V电池和电动自行车应用中的线路电压和负载调节.pdf》资料免费下载
    发表于 09-06 09:45 2次下载
    <b class='flag-5'>优化</b>汽车<b class='flag-5'>类</b>48V电池和电动自行车应用中的线路电压和负载调节

    一款3605电源芯片的性能优化与改进思路

    在电源设计中,确保电源转换器在各种工作条件下都能提供稳定和高效的性能至关重要。 ASP3605是一款高效同步降压转换器,它提供多种调节选项,以优化电源的性能。特别是,ITH(Erro
    发表于 08-23 14:34

    java子类可以继承父的什么

    Java是一种面向对象的编程语言,它支持继承的概念。继承是面向对象编程的三大特性之一,它允许一个(称为子类或派生)继承另一个(称为父
    的头像 发表于 08-20 11:02 1312次阅读

    倾斜光栅的鲁棒性优化

    摘要 ** 由于制造过程中潜在的不准确性,对于一个好的光栅设计来说,面对光栅参数的微小变化,提供稳健的结果是至关重要的。VirtualLab Fusion为光学工程师提供各种工具,可以将
    发表于 08-12 18:38

    聊聊JVM如何优化

    进行优化。 1.JVM内存模型 针对JAVA8的模型进行讨论,JVM的内存模型主要分为几个关键区域:堆、方法区、程序计数器、虚拟机栈和本地方法栈。堆内存进一步细分为年轻代、老年代,年轻代按其特性又分为E区,S1和S2区。关于内存模型的一些细节就
    的头像 发表于 08-05 17:49 496次阅读
    聊聊JVM如何<b class='flag-5'>优化</b>

    华纳云:java web和java有什么区别java web和java有什么区别

    Java Web和Java是两个不同的概念,它们在功能、用途和实现方式上存在一些区别,下面将详细介绍它们之间的区别。 1. 功能和用途: – Java是一种编程语言,它提供
    的头像 发表于 07-16 13:35 833次阅读
    华纳云:<b class='flag-5'>java</b> web和<b class='flag-5'>java</b>有什么区别<b class='flag-5'>java</b> web和<b class='flag-5'>java</b>有什么区别

    JDK8升级JDK11最全实践干货来了

    1、前言 截至目前(2023年),Java8发布至今已有9年,2018年9月25日,Oracle发布Java11,这是Java8之后的首个LTS版本。那么从JDK
    的头像 发表于 06-25 14:51 464次阅读
    JDK<b class='flag-5'>8</b>升级JDK11最全实践干货来了

    请问handle的写法

    遇到一行代码 YDLidar *laser = lidarCreate(); //Create a handle to this Lidar. 请问这行代码是什么意思,句柄的写法怎么是这样的呀
    发表于 05-18 10:42

    在官方库中发现GPIOx-&gt;ODR这种写法,是什么意思,怎么中间会有-&gt;?

    本人新手,刚接触st的芯片 在官方库中发现GPIOx->ODR这种写法,不知道这是什么意思,怎么中间会有->? 请论坛上高手予以帮忙解答
    发表于 05-10 06:48

    的网线能用六水晶头吗

    网线不能使用六或七水晶头。虽然六和七水晶头与八网线在结构上有相似之处,但是它们的性
    的头像 发表于 04-18 11:45 1918次阅读

    java实现多线程的几种方式

    多种实现多线程的方式,本文将详细介绍以下几种方式: 1.继承Thread 2.实现Runnable接口 3.Callable和Future 4.线程池 5.Java 8
    的头像 发表于 03-14 16:55 738次阅读

    浅谈代码优化与过度设计

    本文记录了作者从“代码优化”到“过度设计”的典型思考过程,这过程中涉及很多Java的语法糖及设计模式的东西,很典型,能启发思考,遂记录下来。 有一天Review师妹的代码,看到一行很难看的代码
    的头像 发表于 01-19 10:05 560次阅读
    浅谈代码<b class='flag-5'>优化</b>与过度设计