Java 8新特性(三):Optional类

栏目: Java · 发布时间: 7年前

内容简介:Java 8新特性(三):Optional类
Java 8新特性(三):Optional类

本文首发于一书生VOID的博客。 原文链接: Java 8新特性(三):Optional类

在上一篇介绍Stream流式数据处理的文章中提到了 Optional 类,这是 Java 8新增的一个类,用以解决程序中常见的 NullPointerException 异常问题。本篇文章将详细介绍 Optional 类,以及如何用它消除代码中的 null 检查。

避免使用 null 检查

作为Java开发人员,几乎所有人都遇到过 NullPointerException 异常,大多数人遇到 NullPointerException 异常时都会在异常出现的地方加上 if 代码块来判断值不为空,比如下面的代码:

public void bindUserToRole(User user) {
    if (user != null) {
        String roleId = user.getRoleId();
        if (roleId != null) {
            Role role = roleDao.findOne(roleId);
            if (role != null) {
                role.setUserId(user.getUserId());
                roleDao.save(role);
            }
        }
    }
}

这是比较普遍的做法,为了避免出现 NullPointerException 异常,手动对可能为 null 值进行了处理,不过代码看起来非常糟糕,业务逻辑被淹没在 if 逻辑判断中,也许下面的代码看起来可读性稍好一些:

public String bindUserToRole(User user) {
    if (user == null) {
        return;
    }

    String roleId = user.getRoleId();
    if (roleId == null) {
        return;
    }

    Role = roleDao.findOne(roleId);
    if (role != null) {
        role.setUserId(user.getUserId());
        roleDao.save(role);
    }
}

上面的代码避免了深层的 if 语句嵌套,但本质上是一样的,方法内有三个不同的返回点,出错后调试也不容易,因为你不知道是那个值导致了 NullPointerException 异常。

基于上面的原因,Java 8中引入了一个新的类 Optional ,用以避免使用 null 值引发的种种问题。

Optional

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

创建 Optional 对象

Optional 类提供类三个方法用于实例化一个 Optional 对象,它们分别为 empty()of()ofNullable() ,这三个方法都是静态方法,可以直接调用。

empty() 方法用于创建一个没有值的 Optional 对象:

Optional<String> emptyOpt = Optional.empty();

empty() 方法创建的对象没有值,如果对 emptyOpt 变量调用 isPresent() 方法会返回 false ,调用 get() 方法抛出 NullPointerException 异常。

of() 方法使用一个非空的值创建 Optional 对象:

String str = "Hello World";
Optional<String> notNullOpt = Optional.of(str);

ofNullable() 方法接收一个可以为 null 的值:

Optional<String> nullableOpt = Optional.ofNullable(str);

如果 str 的值为 null ,得到的 nullableOpt 是一个没有值的 Optional 对象。

提取 Optional 对象中的值

如果我们要获取 User 对象中的 roleId 属性值,常见的方式是直接获取:

String roleId = null;
if (user != null) {
    roleId = user.getRoleId();
}

使用 Optional 中提供的 map() 方法可以以更简单的方式实现:

Optional<User> userOpt = Optional.ofNullable(user);
Optional<String> roleIdOpt = userOpt.map(User::getRoleId);

使用 orElse() 方法获取值

Optional 类还包含其他方法用于获取值,这些方法分别为:

  • orElse() :如果有值就返回,否则返回一个给定的值作为默认值;
  • orElseGet() :与 orElse() 方法作用类似,区别在于生成默认值的方式不同。该方法接受一个 Supplier<? extends T> 函数式接口参数,用于生成默认值;
  • orElseThrow() :与前面介绍的 get() 方法类似,当值为 null 时调用这两个方法都会抛出 NullPointerException 异常,区别在于该方法可以指定抛出的异常类型。

下面来看看这三个方法的具体用法:

String str = "Hello World";
Optional<String> strOpt = Optional.of(str);
String orElseResult = strOpt.orElse("Hello Shanghai");
String orElseGet = strOpt.orElseGet(() -> "Hello Shanghai");
String orElseThrow = strOpt.orElseThrow(
        () -> new IllegalArgumentException("Argument 'str' cannot be null or blank."));

此外, Optional 类还提供了一个 ifPresent() 方法,该方法接收一个 Consumer<? super T> 函数式接口,一般用于将信息打印到控制台:

Optional<String> strOpt = Optional.of("Hello World");
strOpt.ifPresent(System.out::println);

使用 filter() 方法过滤

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

Optional<String> optional = Optional.of("lw900925@163.com");
optional = optional.filter(str -> str.contains("164"));

在上面的代码中,如果 filter() 方法中的Lambda表达式成立, filter() 方法会返回当前 Optional 对象值,否则,返回一个值为空的 Optional 对象。

如何正确使用 Optional

通过上面的例子可以看出, Optional 类可以优雅的避免 NullPointerException 带来的各种问题,不过,你是否真正掌握了 Optional 的用法?假设你试图使用 Optional 来避免可能出现的 NullPointerException 异常,编写了如下代码:

Optional<User> userOpt = Optional.ofNullable(user);
if (userOpt.isPresent()) {
    User user = userOpt.get();
    // do something...
} else {
    // do something...
}

坦白说,上面的代码与我们之前的使用 if 语句判断空值没有任何区别,没有起到 Optional 的正真作用:

if (user != null) {
    // do something...
} else {
    // do something...
}

当我们从之前版本切换到Java 8的时候,不应该还按照之前的思维方式处理 null 值,Java 8提倡函数式编程,新增的许多API都可以用函数式编程表示, Optional 类也是其中之一。这里有几条关于 Optional 使用的建议:

  1. 尽量避免在程序中直接调用 Optional 对象的 get()isPresent() 方法;
  2. 避免使用 Optional 类型声明实体类的属性;

第一条建议中直接调用 get() 方法是很危险的做法,如果 Optional 的值为空,那么毫无疑问会抛出 NullPointerException 异常,而为了调用 get() 方法而使用 isPresent() 方法作为空值检查,这种做法与传统的用 if 语句块做空值检查没有任何区别。

第二条建议避免使用 Optional 作为实体类的属性,它在设计的时候就没有考虑过用来作为类的属性,如果你查看 Optional 的源代码,你会发现它没有实现 java.io.Serializable 接口,这在某些情况下是很重要的(比如你的项目中使用了某些序列化框架),使用了 Optional 作为实体类的属性,意味着他们不能被序列化。

下面我们通过一些例子讲解 Optional 的正确用法:

正确创建 Optional 对象

上面提到创建 Optional 对象有三个方法, empty() 方法比较简单,没什么特别要说明的。主要是 of()ofNullable() 方法。当你很确定一个对象不可能为 null 的时候,应该使用 of() 方法,否则,尽可能使用 ofNullable() 方法,比如:

public static void method(Role role) {
    // 当Optional的值通过常量获得或者通过关键字new初始化,可以直接使用of()方法
    Optional<String> strOpt = Optional.of("Hello World");
    Optional<User> userOpt = Optional.of(new User());

    // 方法参数中role值不确定是否为null,使用ofNullable()方法创建
    Optional<Role> roleOpt = Optional.ofNullable(role);
}

orElse() 方法的使用

return str != null ? str : "Hello World"

上面的代码表示判断字符串 str 是否为空,不为空就返回,否则,返回一个常量。使用 Optional 类可以表示为:

return strOpt.orElse("Hello World")

简化 if-else

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

上面的代码可以简化成:

User user = ...
Optional<User> userOpt = Optional.ofNullable(user);

return user.map(User::getUserName)
            .map(String::toUpperCase)
            .orElse(null);

总结一下,新的 Optional 类让我们可以以函数式编程的方式处理 null 值,抛弃了Java 8之前需要嵌套大量 if-else 代码块,使代码可读性有了很大的提高。下一篇文章将介绍Java 8中新添加的日期API。


以上所述就是小编给大家介绍的《Java 8新特性(三):Optional类》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

数据分析技术白皮书

数据分析技术白皮书

伍海凤、刘鹏、杨佳静、马师慧Sara、李博、Shirley Song、Zinc、李晓艳 / 2016-8-11 / 0

关于数据分析技术白皮书(Analytics Book 中文版),主要内容围绕: 1. 分析(Analytics):网站分析 & APP分析 2. 谷歌分析工具的原理、部署与使用 3. 开源网站分析工具的原理、部署与使用 4. Log日志分析原理 5. 网站分析的维度与指标定义 6. 如何炼成为一个互联网数据分析师 请访问书的数据分析技术白皮书官网“免费”阅......一起来看看 《数据分析技术白皮书》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具