作者 | BoCong-Den

责编 | 夕颜

封图 | CSDN下载自东方IC

出品 | CSDN(ID:CSDNnews)

写在前面

从 Java 8 引入的一个很有趣的特性是 Optional 类。Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException)这个异常就不多说了,肯定是每个 Java 程序员都非常了解的异常。Optional 的完整路径是 java.util.Optional,使用它是为了避免代码中的 if (obj != null) { } 这样范式的代码,可以采用链式编程的风格。而且通过 Optional 中提供的 filter 方法可以判断对象是否符合条件,在符合条件的情况下才会返回,map 方法可以在返回对象前修改对象中的属性。

Optional 的用处

本质上,Optional是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空。我们要知道,Optional 是 Java 实现函数式编程的强劲一步,并且帮助在范式中实现。但是 Optional 的意义显然不止于此。我们知道,任何访问对象方法或属性的调用都可能导致 NullPointerException,在这里,我举个简单的例子来说明一下

1String result = test.getName().getTime().getNum().getAnswer();

在上面的这个代码中,如果我们需要确保不触发异常,就得在访问每一个值之前对其进行明确地检查,就是使用if else对test等值进行判断是否为null,这很容易就变得冗长,难以维护。为了简化这个过程,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,并鼓励程序员写更干净的代码。Optional实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

Optional 的构造函数

Optional 的三种构造方式:Optional.of(obj), Optional.ofNullable(obj) 和明确的 Optional.empty()

  • Optional.of(obj):它要求传入的 obj 不能是 null 值的, 否则直接报NullPointerException 异常。

  • Optional.ofNullable(obj):它以一种智能的,宽容的方式来构造一个 Optional 实例。来者不拒,传 null 进到就得到 Optional.empty(),非 null 就调用 Optional.of(obj).

  • Optional.empty():返回一个空的 Optional 对象

Optional 的常用函数

  • of:为非null的值创建一个Optional。of方法通过工厂方法创建Optional类。需要注意的是,创建对象时传入的参数不能为null。如果传入参数为null,则抛出NullPointerException。因此不经常用。

  • ofNullable:为指定的值创建一个Optional,如果指定的值为null,则返回一个空的Optional。

  • isPresent:如果值存在返回true,否则返回false。

  • ifPresent:如果Optional实例有值则为其调用consumer,否则不做处理

  • get:如果Optional有值则将其返回,否则抛出NoSuchElementException。因此也不经常用。

  • orElse:如果有值则将其返回,否则返回指定的其它值。

  • orElseGet:orElseGet与orElse方法类似,区别在于得到的默认值。orElse方法将传入的字符串作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值

  • orElseThrow:如果有值则将其返回,否则抛出supplier接口创建的异常。

  • filter:如果有值并且满足断言条件返回包含该值的Optional,否则返回空Optional。

  • map:如果有值,则对其执行调用mapping函数得到返回值。如果返回值不为null,则创建包含mapping返回值的Optional作为map方法返回值,否则返回空Optional。

  • flatMap:如果有值,为其执行mapping函数返回Optional类型返回值,否则返回空Optional。

Optional 应该怎样用

在使用 Optional 的时候需要考虑一些事情,以决定什么时候怎样使用它。重要的一点是 Optional 不是 Serializable。因此,它不应该用作类的字段。如果你需要序列化的对象包含 Optional 值,Jackson 库支持把 Optional 当作普通对象。也就是说,Jackson 会把空对象看作 null,而有值的对象则把其值看作对应域的值。这个功能在 jackson-modules-java8 项目中。Optional 主要用作返回类型,在获取到这个类型的实例后,如果它有值,你可以取得这个值,否则可以进行一些替代行为。Optional 类可以将其与流或其它返回 Optional 的方法结合,以构建流畅的API。我们来看一个示例,我们不使用Optional写代码是这样的:

1public String getName(User user){
2    if(user == null){
3        return "Unknown";
4    }else return user.name();
5}

接着我们来改造一下上面的代码,使用Optional来改造,我们先来举一个Optional滥用,没有达到流畅的链式API,反而复杂的例子,如下:

1public String getName(User user){
2    Optional<User> u = Optional.ofNullable(user);
3    if(!u.isPresent()){
4        return "Unknown";
5    }else return u.get().name();
6}

这样改写非但不简洁,而且其操作还是和第一段代码一样。无非就是用isPresent方法来替代原先user==null。这样的改写并不是Optional正确的用法,我们再来改写一次。

1public String getName(User user){
2    return Optional.ofNullable(user)
3                            .map(u -> u.name)
4                            .orElse("Unknown");
5}

这样才是正确使用Optional的姿势。那么按照这种思路,我们可以安心的进行链式调用,而不是一层层判断了。当然,我们还可以通过getter方式,对代码进行进一步缩减(前提是User要有getter方法哦),如下

1String result = Optional.ofNullable(user)
2    .flatMap(User::getAddress)
3    .flatMap(Address::getCountry)
4    .map(Country::getIsocode)
5    .orElse("default");

Optional 最佳实践

首先我们先上一张图,来简述一下Optional的使用时机:

  • 避免使用Optional.isPresent()来检查实例是否存在(上面的举例中提到过),因为这种方式和null != obj没有区别,这样用就没什么意义了。

  • 避免使用Optional.get()方式来获取实例对象,因为使用前需要使用Optional.isPresent()来检查实例是否存在,否则会出现NoSuchElementException异常问题。所以使用orElse(),orElseGet(),orElseThrow()获得你的结果

这里要说明一下的是orElse(…)是急切计算,意味着类似下面代码:

1Optional<Dog> optionalDog = fetchOptionalDog();
2optionalDog
3 .map(this::printUserAndReturnUser)
4 .orElse(this::printVoidAndReturnUser)

如果值存在则将执行两个方法,如果值不存在,则仅执行最后一个方法。为了处理这些情况,我们可以使用方法orElseGet(),它将supplier 作为参数,并且是惰性计算的。

  • 避免使用Optional作为类或者实例的属性,而应该在返回值中用来包装返回实例对象。

  • 避免使用Optional作为方法的参数,原因同3。

  • 不要将null赋给Optional

  • 只有每当结果不确定时,使用Optional作为返回类型,从某种意义上讲,这是使用Optional的唯一好地方,用java官方的话讲就是:我们的目的是为库方法的返回类型提供一种有限的机制,其中需要一种明确的方式来表示“无结果”,并且对于这样的方法使用null 绝对可能导致错误。

  • 不要害怕使用map和filter,有一些值得遵循的一般开发实践称为SLA-p:Single Layer of Abstraction字母的第一个大写。下面是需要被重构代码到重构的代码

示例一

 1Dog dog = fetchSomeVaraible();2String dogString = dogToString(dog);3public String dogToString(Dog dog){4 if(dog == null){5   return "DOG'd name is : " + dog.getName();6 } else { 7   return "CAT";8 }9}
10//上面代码重构到下面代码
11Optional<Dog> dog = fetchDogIfExists();
12String dogsName = dog
13 .map(this::convertToDog)
14 .orElseGet(this::convertToCat)
15
16public void convertToDog(Dog dog){
17   return "DOG'd name is : " + dog.getName();
18}
19
20public void convertToCat(){
21   return "CAT";
22}

示例二

1Dog dog = fetchDog();
2if(optionalDog != null && optionalDog.isBigDog()){
3  doBlaBlaBla(optionalDog);
4}
5//上面代码重构到下面代码
6Optional<Dog> optionalDog = fetchOptionalDog();
7optionalDog
8 .filter(Dog::isBigDog)
9 .ifPresent(this::doBlaBlaBla)
  • 不要为了链方法而使用optional。使用optional 时要注意的一件事是链式方法的诱惑。当我们像构建器模式一样链接方法时,事情可能看起来很漂亮。但并不总是等于更具可读性。所以不要这样做,它对性能不利,对可读性也不好。我们应尽可能避免使用null引用。

1Dog dog = fetchDog();
2if(optionalDog != null && optionalDog.isBigDog()){
3  doBlaBlaBla(optionalDog);
4}
5//上面代码重构到下面代码
6Optional<Dog> optionalDog = fetchOptionalDog();
7optionalDog
8 .filter(Dog::isBigDog)
9 .ifPresent(this::doBlaBlaBla)
  • 使所有表达式成为单行lambda。这是更普遍的规则,我认为也应该应用于流。但这篇文章是关于optional 。使用Optional 重要点是记住等式左边和右边一样重要,这里举个例子

 1Optional2 .ofNullable(someVariable)3 .map(variable -> {4   try{5      return someREpozitory.findById(variable.getIdOfOtherObject());6   } catch (IOException e){7     LOGGER.error(e); 8     throw new RuntimeException(e); 9   }})
10 .filter(variable -> {
11   if(variable.getSomeField1() != null){
12     return true;
13   } else if(variable.getSomeField2() != null){
14     return false;
15   } else {
16     return true;
17   }
18  })
19 .map((variable -> {
20   try{
21      return jsonMapper.toJson(variable);
22   } catch (IOException e){
23     LOGGER.error(e);
24     throw new RuntimeException(e);
25   }}))
26 .map(String::trim)
27 .orElseThrow(() -> new RuntimeException("something went horribly wrong."))

上面那么冗长代码块可以使用方法替代:

1Optional
2 .ofNullable(someVariable)
3 .map(this::findOtherObject)
4 .filter(this::isThisOtherObjectStale)
5 .map(this::convertToJson)
6 .map(String::trim)
7 .orElseThrow(() -> new RuntimeException("something went horribly wrong."));

原文链接:

https://blog.csdn.net/DBC_121/article/details/104984093

CSDN VIP会员卡新增权益啦!!!

数百本电子书现在免费阅读啦!下载全站资源,免费观看千门课程。每日学习仅需0.8元。

【End】

「AI大师课」是CSDN发起的“百万人学AI”倡议下的重要组成部分,4月份AI大师课以线上技术峰会的形式推出,来自微软、硅谷TigerGraph、北邮等产学界大咖图计算+机器学习,语音技术、新基建+AI、AI+医疗等主题展开分享,扫描下方二维码免费报名,限时再送299元「2020AI开发者万人大会」门票一张。

推荐阅读 

☞暴风集团仅剩10余人;搜狗告百度输入法侵权案再驳回;Linux 5.6发布 | 极客头条

☞自然语言模型算法太杂乱?国产统一 AI 开源框架来了!

☞华为开发者大会HDC.Cloud技术探秘:云搜索服务技术实践

☞北上广深房价平均上涨 3.5%,程序员如何靠自己安家?

☞一文教你如何使用 MongoDB 和 HATEOAS 创建 REST Web 服务

☞BTC重现“自由落体”式暴跌,原来是受这几个因素影响?

你点的每一个在看,我认真当成了喜欢

点击阅读原文,参与报名!

Optional 是个好东西,你会用么?| 原力计划相关推荐

  1. 看我发现了什么好东西? Java Optional,绝对值得一学 | 原力计划

    作者 | 沉默王二 来源 | CSDN博客 头图 | 付费下载自视觉中国 出品 | CSDN(ID:CSDNnews) 想学习,永远都不晚,尤其是针对 Java 8 里面的好东西,Optional 就 ...

  2. 为什么微博用jsoup爬取不出来东西_腾讯面试题: 百度搜索为什么那么快? | 原力计划...

    作者 | 小松与蘑菇 责编 | 王晓曼 出品 | CSDN博客 我还记得去年面腾讯时,面试官最后轻飘飘地问:百度/Google的搜索为什么那么快? 这个问题我懵了,我从来没想过,搜素引擎的原理是什么. ...

  3. 藏宝阁显示角色可买服务器,梦幻西游:刚买角色的藏宝阁上居然有东西,客服说是原号主出售的...

    游戏的意义就在于它能够给人带来快乐,如果过多的掺杂其他的东西就失去了其本身的意义,大家好,我是小三,每天给大家分享游戏中的八卦趣事. 梦幻西游是2003年的时候上线的一款游戏,那个时候上线的梦幻还有跟 ...

  4. Optional 是个好东西,你真的会用么?

    点击关注公众号,Java干货及时送达  作者:zjhred blog.csdn.net/zjhred/article/details/84976734 引言 在文章的开头,先说下NPE问题,NPE问题 ...

  5. Optional是个好东西,你会用么?(全面深度解析)

    写在前面 从 Java 8 引入的一个很有趣的特性是 Optional 类.Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException)这个异常就不多说了,肯定 ...

  6. 让 AI 训练 AI:揭秘阿里、浙大的 AI 训练师助手

    不久前,人力资源社会保障部发布了一种炙手可热的新职业:AI训练师.没想到,浙江大学与阿里安全的人工智能训练师马上创造出一个 "AI训练师助手",高效打造AI深度模型,应对海量应用场 ...

  7. BTC 重现“自由落体”式暴跌,原来是受这几个因素影响?

    来源 | Billy Bambrough 译者 | 火火酱 责编 | 徐威龙 出品 | 区块链大本营(blockchain_camp) 本文为区块链大本营翻译,旨在为读者提供更多市场声音,文中部分内容 ...

  8. 面试官:你说对 MySQL 事务很熟?那我问你 10 个问题

    作者 | LemonCoder 责编 | 胡巍巍 本文系作者投稿 学习关系型数据库MySQL是很好的切入点,大部分人学习和工作中用惯了CRUD,对面试官刨根问底的灵魂拷问你还能对答如流吗?我们有必要了 ...

  9. 75.58 亿美元成交!美国最大规模 5G 毫米波频谱拍卖

    作者 | 马超 责编 | 胡巍巍 出品 | CSDN(ID:CSDNnews) 美国三大股指在近日迎来了两次史诗级的暴跌,先是3月9日道指下跌近1900点,标普500暴跌7%,双双熔断:后是3月12日 ...

最新文章

  1. 现代hy-9600音响_从音响工程师到软件工程师-为什么我要学习编码
  2. Python基础学习之 函数
  3. Linux 小知识翻译 - 「代理服务器」
  4. 自动化测试框架设计模式
  5. C#连接MySql数据库的方法
  6. Android 布局中 如何使控件居中
  7. 【Docker1】指令,docker-compose,Dockerfile,容器编排工具k8s
  8. angular - 如何运行在起来 - 使用nginx
  9. 笨办法学 Python · 续 引言
  10. sample语言词法分析_Go 译文之词法分析与解析 Part Three
  11. Spark中的数据本地性
  12. springboot全局异常处理_SpringBoot:如何优雅地处理全局异常
  13. Mac OS开启黑暗模式
  14. [LeetCode]Shortest Palindrome
  15. MBSE基于模型的系统工程
  16. iphone邮件服务器 263,在iphone上怎么设置263邮箱
  17. roaringbitmap java,数据结构-RoaringBitmap概要
  18. 怎么用计算机磁盘管理分区,在win 7中如何用磁盘管理为硬盘分区呢?
  19. lzw压缩 java_Java压缩之LZW算法字典压缩与解压讲解
  20. jQuery中的get和post请求

热门文章

  1. Linux学习-逻辑滚动条管理员 (Logical Volume Manager)
  2. startService和onBinderService混合开发音乐播放器
  3. Python3之Memcache使用
  4. [论文阅读] iCaRL: Incremental Classifier and Representation Learning
  5. [论文阅读] Transformer Transforms Salient Object Detection and Camouflaged Object Detection
  6. 【杂文】【python】Python 对象的析构
  7. 前馈神经网络对mnist数据集实战
  8. php数组的奇数_PHP - 查找数组元素是奇数还是偶数
  9. leetcode题库10--正则表达式匹配
  10. Unity5 Space Shooter基础部分开发笔记