本篇文章将详细介绍 Optional 类,以及如何用它消除代码中的 null 检查。在开始以前首先来看下什么是 NPE,以及在 Java 8 以前是如何处理 NPE 问题的。java
空指针异常(NullPointException,简称 NPE)能够说是全部 Java 程序员都遇到过的一个异常,虽然 Java 从设计之初就力图让程序员脱离指针的苦海,可是指针确实是实际存在的,而 Java 设计者也只能是让指针在 Java 语言中变得更加简单易用,而不能彻底剔除,因此才有了常见对的关键字 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);
}
}复制代码
可是这样的应对方式会让程序出现愈来愈多的 null 断定,一个良好的程序设计,应该让代码中尽可能少出现 null 关键字,所以 Java 8 引入 Optional 类来避免 NPE 问题,同时也提高了代码的美观度。但并非对 null 关键字的一种替代,而是对于 null 断定提供了一种更加优雅的实现,从而避免 NPE 问题。函数式编程
为了更好的解决和避免常见的 NPE 问题,Java 8 中引入了一个新的类 java.util.Optional
Optional 类提供类三个方法用于实例化一个 Optional 对象,它们分别为 empty()、of()、ofNullable(),这三个方法都是静态方法,能够直接调用。spa
empty() 方法用于建立一个没有值的Optional对象:设计
Optional<String> emptyOpt = Optional.empty();复制代码
empty() 方法建立的对象没有值,若是对 emptyOpt 变量调用 isPresent() 方法会返回 false,调用 get() 方法抛出 NPE 异常。指针
of() 方法使用一个非空的值建立Optional对象:code
String str = "Hello World";
Optional<String> notNullOpt = Optional.of(str);复制代码
ofNullable() 方法接收一个能够为null的值:对象
Optional<String> nullableOpt = Optional.ofNullable(str);复制代码
若是 str 的值为 null,获得的 nullableOpt 是一个没有值的 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 类还包含其余方法用于获取值,这些方法分别为:
下面来看看这三个方法的具体用法:
String str = "Hello World";
Optional<String> strOpt = Optional.of(str);
String orElseResult = strOpt.orElse("Hello BeiJing");
String orElseGet = strOpt.orElseGet(() -> "Hello BeiJing");
String orElseThrow = strOpt.orElseThrow(
() -> new IllegalArgumentException("Argument 'str' cannot be null or blank."));复制代码
此外,Optional 类还提供了一个 ifPresent() 方法,该方法接收一个 Consumer 函数式接口,通常用于将信息打印到控制台:
Optional<String> strOpt = Optional.of("Hello World");
strOpt.ifPresent(System.out::println);复制代码
使用 filter() 方法过滤
filter() 方法可用于判断 Optional 对象是否知足给定条件,通常用于条件过滤:
Optional<String> optional = Optional.of("wupx94@qq.com");
optional = optional.filter(str -> str.contains("wupx"));复制代码
在上面的代码中,若是 filter() 方法中的 Lambda 表达式成立,filter() 方法会返回当前 Optional 对象值,不然,返回一个值为空的 Optional 对象。
关于 Optional 使用建议:
- 尽可能避免在程序中直接调用 Optional 对象的 get() 和 isPresent() 方法
- 避免使用 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 是一个 final 类,未实现任何接口,Optional 不能序列化,不能做为类的字段(field),因此当咱们在利用该类包装定义类的属性的时候,若是咱们定义的类有序列化的需求,那么由于 Optional 没有实现 Serializable 接口,这个时候执行序列化操做就会有问题:
import java.util.Optional;
import lombok.Data;
@Data
public class User implements Serializable {
private String name;
private String gender;
private Optional<String> phone; // 不能序列化
}复制代码
能够经过本身实现 getter 方法,使 Lomok 不自动生成,以下:
import java.util.Optional;
import lombok.Data
@Data
public class User implements Serializable {
private String name;
private String gender;
private String phone;
public Optional<String> getPhone() {
return Optional.ofNullable(phone);
}
}复制代码
Java 8 中 Optional 类可让咱们以函数式编程的方式处理 null 值,抛弃了 Java 8 以前须要嵌套大量 if-else 代码块,使代码可读性有了很大的提升,可是应尽可能避免使用 Optional 类型声明实体类的属性。
本文由博客一文多发平台 OpenWrite 发布!