一、使用Optional引言

1.1、代码问题引出

在写程序的时候一般都遇到过 NullPointerException,所以经常会对程序进行非空的判断:

User user = getUserById(id);
if (user != null) {
String username = user.getUsername();
System.out.println("Username is: " + username); // 使用 username
}

为了解决这种尴尬的处境,JDK 终于在 Java8 的时候加入了 Optional 类,查看 Optional 的 javadoc 介绍:

A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.

这是一个可以包含或者不包含非 null 值的容器。如果值存在则 isPresent()方法会返回 true,调用 get() 方法会返回该对象。

1.2、解决进阶

我们假设 getUserById 已经是个客观存在的不能改变的方法,那么利用 isPresentget 两个方法,我们现在能写出下面的代码:

Optional<User> user = Optional.ofNullable(getUserById(id));
if (user.isPresent()) {
String username = user.get().getUsername();
System.out.println("Username is: " + username); // 使用 username
}

好像看着代码是优美了点,但是事实上这与之前判断 null 值的代码没有本质的区别,反而用 Optional 去封装 value,增加了代码量。所以我们来看看 Optional 还提供了哪些方法,让我们更好的(以正确的姿势)使用 Optional

二、Optional三个静态构造方法

1)概述:

JDK 提供三个静态方法来构造一个 Optional

  1. Optional.of(T value)

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

    该方法通过一个非 nullvalue 来构造一个 Optional,返回的 Optional 包含了 value 这个值。对于该方法,传入的参数一定不能为 null,否则便会抛出 NullPointerException

  2. Optional.ofNullable(T value)

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

    该方法和 of 方法的区别在于,传入的参数可以为 null —— 但是前面 javadoc 不是说 Optional 只能包含非 null 值吗?我们可以看看 ofNullable 方法的源码。

    原来该方法会判断传入的参数是否为 null,如果为 null 的话,返回的就是 Optional.empty()

  3. Optional.empty()

        public static<T> Optional<T> empty() {
    @SuppressWarnings("unchecked")
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
    }

    该方法用来构造一个空的 Optional,即该 Optional 中不包含值 —— 其实底层实现还是 如果 Optional 中的 value 为 null 则该 Optional 为不包含值的状态,然后在 API 层面将 Optional 表现的不能包含 null值,使得 Optional 只存在 包含值不包含值 两种状态。

2)分析:

前面 javadoc 也有提到,OptionalisPresent() 方法用来判断是否包含值,get() 用来获取 Optional 包含的值 —— 值得注意的是,如果值不存在,即在一个Optional.empty 上调用 get() 方法的话,将会抛出 NoSuchElementException异常

3)总结:

1)Optional.of(obj): 它要求传入的 obj 不能是 null 值的, 否则还没开始进入角色就倒在了 NullPointerException 异常上了.

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

那是不是我们只要用 Optional.ofNullable(obj) 一劳永逸, 以不变应二变的方式来构造 Optional 实例就行了呢? 那也未必, 否则 Optional.of(obj) 何必如此暴露呢, 私有则可。

三、Optional常用方法详解

3.1、Optional常用方法概述

  1. Optional.of(T t)

    将指定值用 Optional 封装之后返回,如果该值为 null,则抛出一个 NullPointerException 异常。

  2. Optional.empty()

    创建一个空的 Optional 实例。

  3. Optional.ofNullable(T t)

    将指定值用 Optional 封装之后返回,如果该值为 null,则返回一个空的 Optional 对象。

  4. isPresent

    如果值存在返回true,否则返回false

  5. ifPresent

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

    要理解ifPresent方法,首先需要了解Consumer类。简答地说,Consumer类包含一个抽象方法。该抽象方法对传入的值进行处理,但没有返回值。 Java8支持不用接口直接通过lambda表达式传入参数。

    如果Optional实例有值,调用ifPresent()可以接受接口段或lambda表达式。

  6. Optional.get()

    如果该值存在,将该值用 Optional 封装返回,否则抛出一个 NoSuchElementException 异常。

  7. orElse(T t)

    如果调用对象包含值,返回该值,否则返回t。

  8. orElseGet(Supplier s)

    如果调用对象包含值,返回该值,否则返回 s 获取的值。

  9. orElseThrow()

    它会在对象为空的时候抛出异常。

  10. map(Function f)

    如果值存在,就对该值执行提供的 mapping 函数调用。

  11. flatMap(Function mapper)

    如果值存在,就对该值执行提供的mapping 函数调用,返回一个 Optional 类型的值,否则就返回一个空的 Optional 对象。

3.2、Optional常用方法详解

3.2.1、ifPresent
    public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}

如果 Optional 中有值,则对该值调用 consumer.accept,否则什么也不做。

所以对于引言上的例子,我们可以修改为:

Optional<User> user = Optional.ofNullable(getUserById(id));
user.ifPresent(u -> System.out.println("Username is: " + u.getUsername()));
3.2.2、orElse
    public T orElse(T other) {
return value != null ? value : other;
}

如果 Optional 中有值则将其返回,否则返回 orElse 方法传入的参数。

User user = Optional
.ofNullable(getUserById(id))
.orElse(new User(0, "Unknown")); System.out.println("Username is: " + user.getUsername());
3.2.3、orElseGet
    public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}

orElseGetorElse 方法的区别在于,orElseGet 方法传入的参数为一个 Supplier 接口的实现 —— 当 Optional 中有值的时候,返回值;当 Optional 中没有值的时候,返回从该 Supplier 获得的值。

User user = Optional
.ofNullable(getUserById(id))
.orElseGet(() -> new User(0, "Unknown")); System.out.println("Username is: " + user.getUsername());
3.2.4、orElseThrow
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}

orElseThroworElse 方法的区别在于,orElseThrow 方法当 Optional 中有值的时候,返回值;没有值的时候会抛出异常,抛出的异常由传入的 exceptionSupplier 提供。

举例说明:

​ 在 SpringMVC 的控制器中,我们可以配置统一处理各种异常。查询某个实体时,如果数据库中有对应的记录便返回该记录,否则就可以抛出 EntityNotFoundException ,处理 EntityNotFoundException 的方法中我们就给客户端返回Http 状态码 404 和异常对应的信息 —— orElseThrow 完美的适用于这种场景。

@RequestMapping("/{id}")
public User getUser(@PathVariable Integer id) {
Optional<User> user = userService.getUserById(id);
return user.orElseThrow(() -> new EntityNotFoundException("id 为 " + id + " 的用户不存在"));
} @ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<String> handleException(EntityNotFoundException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
3.2.5、map
    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));
}
}

如果当前 OptionalOptional.empty,则依旧返回 Optional.empty;否则返回一个新的 Optional,该 Optional 包含的是:函数 mapper 在以 value 作为输入时的输出值。

String username = Optional.ofNullable(getUserById(id))
.map(user -> user.getUsername())
.orElse("Unknown")
.ifPresent(name -> System.out.println("Username is: " + name));

而且我们可以多次使用 map 操作:

Optional<String> username = Optional.ofNullable(getUserById(id))
.map(user -> user.getUsername())
.map(name -> name.toLowerCase())
.map(name -> name.replace('_', ' '))
.orElse("Unknown")
.ifPresent(name -> System.out.println("Username is: " + name));
3.2.6、flatMap
    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));
}
}

flatMap 方法与 map 方法的区别在于,map 方法参数中的函数 mapper 输出的是值,然后 map 方法会使用 Optional.ofNullable 将其包装为 Optional;而 flatMap 要求参数中的函数 mapper 输出的就是 Optional

Optional<String> username = Optional.ofNullable(getUserById(id))
.flatMap(user -> Optional.of(user.getUsername()))
.flatMap(name -> Optional.of(name.toLowerCase()))
.orElse("Unknown")
.ifPresent(name -> System.out.println("Username is: " + name));
3.2.7、filter
    public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}

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

Optional<String> username = Optional.ofNullable(getUserById(id))
.filter(user -> user.getId() < 10)
.map(user -> user.getUsername());
.orElse("Unknown")
.ifPresent(name -> System.out.println("Username is: " + name));

四、Optional使用示例

4.1、使用展示一

当 user.isPresent() 为真, 获得它关联的 orders的映射集合, 为假则返回一个空集合时, 我们用上面的 orElse, orElseGet 方法都乏力时, 那原本就是 map 函数的责任, 我们可以这样一行:

return user.map(u -> u.getOrders()).orElse(Collections.emptyList())

//上面避免了我们类似 Java 8 之前的做法
if(user.isPresent()) {
return user.get().getOrders();
} else {
return Collections.emptyList();
}

map 是可能无限级联的, 比如再深一层, 获得用户名的大写形式:

return user.map(u -> u.getUsername())
.map(name -> name.toUpperCase())
.orElse(null);

以前的做法:

User user = .....
if(user != null) {
String name = user.getUsername();
if(name != null) {
return name.toUpperCase();
} else {
return null;
}
} else {
return null;
}

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

Optional<String> longName = name.filter((value) -> value.length() > 6);
System.out.println(longName.orElse("The name is less than 6 characters"));

Java8之熟透Optional的更多相关文章

  1. Java8 新特性 Optional 类

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

  2. Java8新特性——Optional

    前言 在开发中,我们常常需要对一个引用进行判空以防止空指针异常的出现.Java8引入了Optional类,为的就是优雅地处理判空等问题.现在也有很多类库在使用Optional封装返回值,比如Sprin ...

  3. Java8中的Optional操作

    作者:汤圆 个人博客:javalover.cc 前言 官人们好啊,我是汤圆,今天给大家带来的是<Java8中的Optional操作>,希望有所帮助,谢谢 文章纯属原创,个人总结难免有差错, ...

  4. Java8:使用 Optional 处理 null

    写过 Java 程序的同学,一般都遇到过 NullPointerException :) —— 为了不抛出这个异常,我们便会写如下的代码: User user = getUserById(id); i ...

  5. Java8新特性Optional、接口中的默认方法与静态方法

    Optional Optional 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念 ...

  6. Java8系列 (五) Optional类

    概述 在Java8之前, 如果需要对一个变量做一次 null 检查, 通常会像下面这样写 T t = service1.query(); if (t != null) { K k = service2 ...

  7. Java8新特性--Optional

    Java 8引入了一个新的Optional类.Optional类的Javadoc描述如下: 这是一个可以为null的容器对象.如果值存在则isPresent()方法会返回true,调用get()方法会 ...

  8. JAVA8新特性Optional,非空判断

    Optional java 的 NPE(Null Pointer Exception)所谓的空指针异常搞的头昏脑涨, 有大佬说过 "防止 NPE,是程序员的基本修养." 但是修养归 ...

  9. java8 新特性 -Optional的常见用法

    1. Optional 一. 简介 Opitonal是java8引入的一个新类,目的是为了解决空指针异常问题.本质上,这是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为 ...

随机推荐

  1. mysql 输入show databases 没反应

    我是小白,大佬勿喷 *** mysql 输入show databases 没反应 一句话 不要忘记使用MySQL时在命令后加;哦 * * * 在Linux输入以下命令 mysql 终端显示以下文本 W ...

  2. Docker之- 使用Docker 镜像和仓库

    目录 使用Docker 镜像和仓库 什么是 Docker 镜像 列出 Docker 镜像 tag 标签 Docker Hub 拉取镜像 查找镜像 构建镜像 创建Docker Hub 账号 使用 Doc ...

  3. lnmp环境搭建方法

    网上目前的一键搭建方法: 命令行安装: 1.源码编译安装:(个性化配置,安装配置过程繁琐) 2.使用yum或apt直接安装:(使用编译好的二进制文件安装,速度快) 3.军哥的lnmp一键脚本安装: 4 ...

  4. vue生成element左侧菜单

    首先来总结element ui 官方文档的左侧菜单结构,带有el-submenu为子级节点,el-menu-item表示没有下级.当然,菜单不能写死,因为菜单也许不止两级,所以我们需要递归来实现.根据 ...

  5. net core Webapi 总目录

    目录 博客目录 代码地址 博客目录 net core Webapi基础工程搭建(一)--开发工具及环境 net core Webapi基础工程搭建(二)--创建工程 net core Webapi基础 ...

  6. 盘一盘 NIO (二)—— Channel解析

    Channel是个啥? Channel,顾名思义,它就是一个通道.NIO中的所有IO都是从 Channel 开始的. Channel通道和流非常类似,主要有以下几点区别: 1.流是单向的,通道是双向的 ...

  7. freemarker导出复杂样式的Excel

    freemarker导出复杂样式的Excel 代码地址: gitee https://gitee.com/suveng/demo/tree/master/chapter.002 代码存放于demo下面 ...

  8. 启xin宝app的token算法破解——逆向篇(二)

    启xin宝app的token算法破解--抓包分析篇(一)文章已经对该app进行了抓包分析,现在继续对它进行逆向. 对于一个app而言,我们要逆向app,需要知道什么呢? 逆向工具 Java基础,甚至c ...

  9. 玩转 SpringBoot 2 快速搭建 | IntellJ IDEA篇

     IntellJ IDEA 介绍  IntelliJ IDEA 简称 IDEA,目前被认为是最好用的开发Java 语言开发工具之一.不过是收费的.和其同类型的工具有 Eclipse 和 MyEclip ...

  10. Unity打包——Android和IOS

    推荐阅读:  我的CSDN  我的博客园  QQ群:704621321  我的个人博客 Android包 (1)首先需要安装Android SDK和Java JDK.SDK需要添加tools目录,JD ...