在java开发过程中经常会遇到空指针异常NullPointerExceptions。
NullPointerExceptions是jvm在运行时抛出的RuntimeException。程序中的对象为空的检查容易被开发人员忽略,导致代码中出现严重错误。
在Java 8版本中引入了 Optional
什么是 Optional?
Optional是可能不存在的对象值的容器类型,一个Optional对象不一定包含有值。
下面代码从数据库中获取具有给定id的用户详细信息并返回用户信息对象:
User findUserById(String userId) { ... };
如果数据库中不存在该userId,则方法返回null。我们看看以下调用端代码:
User user = findUserById("667290");
System.out.println("User's Name = " + user.getName());
非常常见的NullPointerException异常。开发人员忘记在代码中添加用户对象是否为空的检查。如果数据库中不存在userId,则上述代码段将抛出一个NullPointerException。
我们看看用Optional类处理在这里遇到NullPointerException的异常:
Optional findUserById(String userId) { ... };
通过从方法返回Optional
客户端代码现在可以改写为:
Optional optional = findUserById("667290");
optional.ifPresent(user -> {
System.out.println("User's name = " + user.getName());
})
一旦有了Optional
上面代码中的ifPresent() 方法的逻辑是,在用户对象存在时调用传入的lambda 表达式,否则不做任何处理。系统现在强制客户端在其代码中写入Optional
创建一个 Optional 实例
1. 创建空的Optional
Optional user = Optional.empty();
空的Optional实例用于在对象值缺失的情况。
2. 创建非空的Optional
User user = new User("667290", "Jim");
Optional userOptional = Optional.of(user);
如果提供给Optional.of()的参数为空,则它将立即抛出一个NullPointerException,并且不会创建Optional对象。
3. 创建值可能为空也可能不为空的Optional
Optional userOptional = Optional.ofNullable(user);
如果传递给Optional.ofNullable()的参数为非空,则返回包含指定值的Optional对象,否则返回空的Optional对象。
检查值是否存在
1.isPresent()
如果Optional对象包含非空值,则isPresent()方法返回true,否则返回false 。
if(optional.isPresent()) {
// value is present inside Optional
System.out.println("Value found - " + optional.get());
} else {
// value is absent
System.out.println("Optional is empty");
}
2.ifPresent()
ifPresent() 方法允许传递在Optional对象中存在值时执行的Consumer接口的实现。如果Optional对象不存在值时什么也不做。
optional.ifPresent(value -> {
System.out.println("Value found - " + value);
});
注意,上述代码ifPresent()方法提供了lambda表达式。这使得代码更加可读和简洁。
使用get() 方法取值
Optional对象的get() 返回其值, 如不存在则抛出NoSuchElementException异常。
User user = optional.get()
我们要避免在没有先检查值是否存在的情况下对Optional使用get()方法,因为如果值不存在,它将引发异常。
使用orElse()返回默认值
orElse() 方法在 Optional值不存在时返回默认值,考虑以下传统的代码处理场景:
// return "Unknown User" if user is null
User finalUser = (user != null) ? user : new User("0", "Unknown User");
现在我们可以使用 Optional的 orElse() 方法实现上述逻辑:
// return "Unknown User" if user is null
User finalUser = optionalUser.orElse(new User("0", "Unknown User"));
使用orElseGet()返回默认值
和orElse()方法不同, orElse()在Optional值为空时是直接返回默认值,而 orElseGet() 允许你传入一个Supplier接口实现,该接口实现提供自定义默认值的功能。
User finalUser = optionalUser.orElseGet(() -> {
return new User("0", "Unknown User");
});
在值为空时引发异常
可以用orElseThrow() 在Optional值为空时抛出异常。一个典型的业务场景是,在REST API接口需要在给定参数查询的对象不存在时返回自定义的 ResourceNotFoundException 异常。
@GetMapping("/users/{userId}")
public User getUser(@PathVariable("userId") String userId) {
return userRepository.findByUserId(userId).orElseThrow(
() -> new ResourceNotFoundException("User not found with userId " + userId);
);
}
使用filter() 方法筛选值
假如有一个Optional
if(user != null && user.getGender().equalsIgnoreCase("MALE")) {
// call a function
}
现在我们用Optional和filter来实现相同的功能:
userOptional.filter(user -> user.getGender().equalsIgnoreCase("MALE"))
.ifPresent(() -> {
// Your function
})
filter()方法接受谓词作为参数。如果Optional包含非空值,且该值与给定谓词匹配,则filter()方法返回具有该值的Optional对象,否则返回空的Optional对象。
因此,仅当Optional
使用map()方法提取和转换值
假设想要获取用户的地址(如果该地址存在),如果该用户来自指定的地方,则打印该地址。
在User对象有getAddress() 方法:
Address getAddress() {
return this.address;
}
传统的做法:
if(user != null) {
Address address = user.getAddress();
if(address != null && address.getCountry().equalsIgnoreCase("India")) {
System.out.println("User belongs to India");
}
}
现在我们使用map()方法实现同样的功能:
userOptional.map(User::getAddress)
.filter(address -> address.getCountry().equalsIgnoreCase("India"))
.ifPresent(() -> {
System.out.println("User belongs to India");
});
上面的代码很简洁,一起来详细理解一下:
//1.使用map()方法提取用户地址。
Optional addressOptional = userOptional.map(User::getAddress)
//2.过滤地址
Optional indianAddressOptional = addressOptional.filter(address -> address.getCountry().equalsIgnoreCase("India"));
// 3.打印输出结果
indianAddressOptional.ifPresent(() -> {
System.out.println("User belongs to India");
});
在上面的示例中, map() 方法在以下情况下会返回空的Optional:1.userOptional的用户信息不存在. 2. 用户存在但 getAdderess() 返回null。
其他情况返回包含用户地址的 Optional
对象.使用flatMap()方法转换值
我们再讨论一下前面 map() 方法的例子. 你可能会问,如果用户的地址可以为空,那么为什么不从getAddress()方法返回一个Optional
对象而是Address对象?我们来看看当 getAddress() 返回Optional
时上述代码:Optional addressOptional = userOptional.map(User::getAddress)
因getAddress() 返回了Optional
, 所以 userOptional.map()将会返回OptionalOptional> addressOptional = userOptional.map(User::getAddress)
这里不需要嵌套的 Optional,可以用 flatMap() 来展开:
Optional addressOptional = userOptional.flatMap(User::getAddress)
如果映射函数返回一个Optional对象,那么就要使用flatMap()而不是map()。