Java8新特性系列我们已经介绍了Stream、Lambda表达式、DateTime日期时间处理,最后以“NullPointerException” 的克星Optional类的讲解来收尾。

背景

作为开发人员每天与NullPointerException斗智斗勇。每接收到参数或调用方法获得值得判断一下是否为null。稍不留意,空指针异常就像幽灵一样出现了。

这篇文章我们来学习Java8是如何通过Optional类来避免空指针异常的。

先来看一下不使用Optional类时,我们为了防止NullPointerException会怎么处理。

public String getParentName(Person son) {
if (son != null) {
Person parent = son.getParent();
if (parent != null) {
return parent.getUsername();
} else {
return "--";
}
} return "--";
}

为了防止出现异常,需要不停的判断对象是否为null。但如果业务逻辑比较复杂,会涌现出大量的ifelse。看似逻辑缜密,但易读性却并不高。

为了解决相关问题,在Effective Java中建议若方法返回类型为集合,则通过返回空集合以避免 NullPointerException,真是煞费苦心。

先看一下上面的代码使用Optional之后会变成什么样子。

public String getParentNameWithOptional(Person son) {
return Optional.ofNullable(son).map(Person::getParent).map(Person::getUsername).orElse("--");
}

对照一下代码,看看神不神奇?!

Optional类简介

java.util.Optional类的引入很好的解决空指针异常,类声明如下:

public final class Optional<T> {}

java.util.Optional类是一个封装了Optional值的容器对象,Optional值可以为null,如果值存在,调用isPresent()方法返回true,调用get()方法可以获取值。

通过源代码会发现,它并没有实现java.io.Serializable接口,因此应避免在类属性中使用,防止意想不到的问题。

除了Optional类之外,还扩展了一些常用类型的Optional对象,比如:OptionalDouble、OptionalInt、OptionalLong。用法基本上相似。

下面通过具体的操作和功能来了解Optional类。

创建Optional对象

创建Optional对象有三种方法:empty()、of()、ofNullable(),均为静态方法。

如果Optional对象没有值则用empty()方法。

Optional empty = Optional.empty();

如果确定Optional对象的值不为null,则可用of()方法。

Optional stringOptional = Optional.of("Hello 公众号:程序新视界");

如果不确定Optional对象的值是否为null,则可用ofNullable()。比如上面,不确定Person对象是不否null,就用了ofNullable()方法。当然,也可以直接给该方法传null。

Optional ofNullOptional = Optional.ofNullable(null);

此时,通过调用其isPresent方法可以查看该Optional中是否值为null。

boolean bool = ofNullOptional.isPresent();
System.out.println(bool);

此时如果直接调用get方法获取值,则会抛出异常。

ofNullOptional.get();

get获取Optional中的值

通过get方法可获取Optional中的值,但如果值为null,则会抛出异常。

Optional ofNullOptional = Optional.ofNullable(null);
ofNullOptional.get();

异常信息:

java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
...

此时,需要另外一个方法的辅助:isPresent()。该方法可判定Optional中是否有值,如果有则返回true,如果没有则返回false。

Optional ofNullOptional = Optional.ofNullable(null);
boolean bool = ofNullOptional.isPresent();
if(bool){
ofNullOptional.get();
}

map获取Optional中的值

对于对象操作,也可以通过map来获取值,最开始简化的例子就是如此。

Optional<Person> sonOptional = Optional.ofNullable(son);
System.out.println(sonOptional.map(Person::getUsername));

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

flatMap获取Optional中的值

如果有值,则返回Optional类型返回值,否则返回空Optional。flatMap与map方法类似。但flatMap中的mapper返回值必须是Optional。调用结束时,flatMap不会对结果用Optional封装。

Optional<Person> sonOptional = Optional.ofNullable(son);
sonOptional.flatMap(OptionalTest::getOptionalPerson);

调用的是当前类OptionalTest的另外一个方法:

public static Optional<Person> getOptionalPerson(Person person){
return Optional.ofNullable(person);
}

orElse获取Optional中的值

orElse方法,如果有值就返回,否则返回一个给定的值作为默认值;

Optional.empty().orElse("--");

上面这种情况就会返回“--”。

在此,这种操作与三目运算效果一样。

str != null ? str : "--"

orElseGet获取Optional中的值

orElseGet()方法与orElse()方法作用类似,但生成默认值的方式不同。该方法接受一个Supplier<? extends T>函数式接口参数,用于生成默认值;

Optional.empty().orElseGet(() -> {
String a = "关注";
String b = "公众号:程序新视界";
return a + b;
});

很显然,这里可以处理更多的业务逻辑。

orElseThrow获取Optional中的值

orElseThrow()方法与get()方法类似,当值为null时调用会抛出NullPointerException异常,但该方法可以指定抛出的异常类型。

Optional.empty().orElseThrow(()-> new RuntimeException("请先关注公众号!"));

此时打印异常信息为:

Optional.empty().orElseThrow(()-> new RuntimeException("请先关注公众号!"));

判断并执行操作

ifPresent方法,可对值进行判断然后打印,接收参数为Consumer<? super T>函数式接口。

Optional.of("公众号:程序新视界").ifPresent(System.out::println);

当然,也可以在函数中执行其他复杂操作:

Optional.of("公众号:程序新视界").ifPresent((val)->{
System.out.println("欢迎关注" + val);
});

filter()方法过滤

filter()方法可用于判断Optional对象是否满足给定条件,一般用于条件过滤:

Optional.of("公众号:程序新视界").filter((val)->{
return val.contains("程序新视界");
});
// 简化写法
Optional.of("公众号:程序新视界").filter((val)-> val.contains("程序新视界"));

使用误区

关于使用Optional的误区有以下:

  • 正确的使用创建方法,不确定是否为null时尽量选择ofNullable方法。
  • 避免用在成员变量上(原因上面已经提到);
  • 避免直接调用Optional对象的get和isPresent方法;

最后一条可能难理解,试想一下如果先用isPresent方法获得是否存在,然后决定是否调用get方法和之前的ifelse判断并无二致。

Java8提倡函数式编程,新增的许多API都可以用函数式编程表示,Optional类也是其中之一。

小结

至此,Java8新特性相关的内容便完结了。

Java8新特性系列相关文章:

关注公众号“程序新视界”,回复“001”,获得整个《Java8新特性系列》的PDF版本。

原文链接:《Java8新特性之空指针异常的克星Optional类

程序新视界:精彩和成长都不容错过
![程序新视界-微信公众号](https://img2018.cnblogs.com/blog/1742867/201910/1742867-20191013111755842-2090947098.png)

Java8新特性之空指针异常的克星Optional类的更多相关文章

  1. Java 8新特性(三):Optional类

    在上一篇介绍Stream流式数据处理的文章中提到了Optional类,这是Java 8新增的一个类,用以解决程序中常见的NullPointerException异常问题.本篇文章将详细介绍Option ...

  2. Java8 新特性 Optional 类

    Optional 类的简介   Optional类的是来自谷歌Guava的启发,然后就加入到Java8新特性中去了.Optional类主要就是为子决解价值亿万的错误,空指针异常.   Optional ...

  3. 【Java8新特性】Optional类在处理空值判断场景的应用 回避空指针异常 编写健壮的应用程序

    一.序言 空值异常是应用运行时常见的异常,传统方式为了编写健壮的应用,常常使用多层嵌套逻辑判断回避空指针异常.Java8新特性之Optional为此类问题提供了优雅的解决方式. 广大程序员朋友对空值异 ...

  4. Java8 新特性 | 如何风骚走位防止空指针异常

    文章整理翻译自 https://winterbe.com/posts/2015/03/15/avoid-null-checks-in-java/ 文章首发于个人网站: https://www.exce ...

  5. 乐字节-Java8新特性之Optional

    上一篇小乐带大家了解了Java新特性之Stream,接下来将会继续述说Java新特性之Optional Optional<T>类(java.util.Optional)是一个容器类,代表一 ...

  6. java8新特性之Optional类

    NullPointException可以说是所有java程序员都遇到过的一个异常,虽然java从设计之初就力图让程序员脱离指针的苦海,但是指针确实是实际存在的,而java设计者也只能是让指针在java ...

  7. 【Java8新特性】不了解Optional类,简历上别说你懂Java8!!

    写在前面 最近,很多读者出去面试都在Java8上栽了跟头,事后自己分析,确实对Java8的新特性一知半解.然而,却在简历显眼的技能部分写着:熟练掌握Java8的各种新特性,能够迅速使用Java8开发高 ...

  8. 【Java8新特性】- Optional应用

    Java8新特性 - Optional应用 生命不息,写作不止 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! ...

  9. Java8新特性之Optional,如何优雅地处理空指针

    是什么 ​ 从 Java 8 引入的一个很有趣的特性是 Optional 类.Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException)-- 每个 Java ...

随机推荐

  1. 单细胞转录组测序技术(scRNA-seq)及细胞分离技术分类汇总

    单细胞测序流程(http://learn.gencore.bio.nyu.edu) 在过去的十多年里,高通量测序技术被广泛应用于生物和医学的各种领域,极大促进了相关的研究和应用.其中转录组测序(RNA ...

  2. 【2】KNN:约会对象分类器

    前言 这是一个KNN算法的应用实例,参考<机器学习实战>中的datingTestSet2.txt的数据集. 可以通过对不同约会对象的特征进行分析然后自动得出以下三种结论: 不喜欢的 有点魅 ...

  3. springcloud超简单的入门2--Eureka服务治理

    Eureka服务治理 下面请听第一个话题,母...咳咳,拿错书了. Eureka简介 eureka是什么呢? 简单来说呢,当我的微服务应用多了起来,一个一个写死再程序里是件很不优雅的事情,而且同一服务 ...

  4. MySQL Explain学习笔记

    目录 一.执行计划概念 二.Explain用法 三.Explain属性介绍 3.1 id属性 3.2 select_type属性 3.3 table属性 3.4 type属性 3.5 possible ...

  5. Collection,泛型,Map

    1.Collection集合 集合和数组的区别? 数组的长度是固定的, 集合的长度是可变的 数组中存储的数据都是同一类型的数据.集合存储的是对象,而且对象的类型可以不一致 集合框架 单列集合 java ...

  6. Linux Shell 基础知识(一)

    1. 本文知识结构 2. shell 基础知识 2.1 shell 简单介绍 ​ GNU bash shell 能提供对 Linux 系统的交互式访问,一般来说,使用快捷键 Ctrl + Alt + ...

  7. Winform中设置DevExpress的RadioGroup的items从配置文件中加载

    场景 DevExpress的RadioGroup的items选项如果是不确定的话,需要其从配置文件中加载. 实现 在项目目录下新建Config文件夹,文件夹下新建xml配置文件. <?xml v ...

  8. 配置phpmyadmin连接远程 MySQL数据库

      引言:1.phpmyadmin程序所在服务器:192.168.1.1,访问地址为:http://192.168.1.1/phpmyadmin2.MySQL数据库所在服务器:192.168.1.2, ...

  9. struts2表单提单细节处理

    1. 上传文件 大部分项目避免不了要上传文件. struts2提供了封闭的上传文件的入口, 网络上也存在大量的插件用于网页表单中上传文件. 由于自己习惯用SSH框架, 所以介绍一下struts2中文件 ...

  10. Java 内存溢出分析

    原文地址:Java 内存溢出分析 博客地址:http://www.moonxy.com 一.前言 Java 的 JVM 的内存一般可分为 3 个区:堆(heap).栈(stack)和方法区(metho ...