如何正确的使用Java8中的Optional类来消除代码中的null检查

Optional类是Java 8新增的一个类,用以解决程序中常见的NullPointerException异常问题。本篇文章将详细介绍Optional类,以及如何用它消除代码中的null检查。java

避免使用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()方法能够获取值。spa

建立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对象。Java知音公众号内回复“后端面试”,送你一份Java面试题宝典。

如何正确使用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使用的建议:

  • 尽可能避免在程序中直接调用Optional对象的get()和isPresent()方法;
  • 避免使用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 userOpt.map(User::getUserName)
            .map(String::toUpperCase)
            .orElse(null);
`

总结一下,新的Optional类让咱们能够以函数式编程的方式处理null值,抛弃了Java 8以前须要嵌套大量if-else代码块,使代码可读性有了很大的提升。

讲到这里,给你们推荐小编经过一些大厂的朋友要到了他们内部的Java面试题,资料可贵,并且仍是近一年的真实面试题;

分别有:蚂蚁金服、拼多多、阿里云、百度、惟品会、携程、丰巢科技、乐信、软通动力、OPPO、银盛支付、中国平安等初,中级,高级Java面试题集合,附带超详细答案,但愿能帮助到你们。

朋友作的秒杀系统被面试官嘲笑!大专毕业,天天都CRUD很难受

小编网盘也经过这些年的积累,把Java电子书也分享给你们,大概有20G左右的资源

朋友作的秒杀系统被面试官嘲笑!大专毕业,天天都CRUD很难受

珍藏多年的230个高端简历模板,也一块儿送给你们

朋友作的秒杀系统被面试官嘲笑!大专毕业,天天都CRUD很难受

资料免费领取步骤

1.备注:思否
2.点点这个连接免费获取:30G架构进阶资料
相关文章
相关标签/搜索