一文彻底搞懂Java8 Optional 怎么用

Java 8 之前的问题

在Java 8 之前,编写一个在特定条件下无法返回值的方法时,有两种方法:一是抛出异常,二是返回null (我们假设方法返回的是对象引用类型),这两种方法都有缺点,抛出异常会捕获整个堆栈轨迹,会有一定的性能开销,返回null 没这缺点,但是会强迫客户端代码有判断值为null 处理代码,如果忽略判断,在运行时可能抛出 NullPointerException 异常。

Optional 简介

java 8 引入了类 Optional , 一个容器类,用 final 修饰,它可以存放一个非 null 的 T 引用,或者什么也不存放。不包含任何内容的optional 成为空(empty),非空的optional 成为存在(present) ,它还提供了一系列便利的方法,后面一一介绍。

Optional 用法

有个方法,根据ID 从数据获取信息

Employee findEmployee(String id)

调用此方法,传参1234,但是在数据库总不存在,会返回null, 如下代码会抛出 NullPointerException

Employee employee = findEmployee("1234");
System.out.println("Employee's Name = " + employee.getName());

我们用 Optional 来解决,防止抛出 NullPointerException ,方法定义如下

Optional < Employee > findEmployee(String id)
Optional < Employee > optional = findEmployee("1234");
optional.ifPresent(employee -> {
 System.out.println("Employee name is " + employee.getName());
})

上面代码的意思是当返回值存在时才打印信息。上面举了一个很普遍的例子来说明optional 的用法,下面详细介绍具体用法。

  1. 创建Optional对象
    当数据库找不到对应信息,需要返回一个空Optional 时,如下方式赋值
Optional < Employee > employee = Optional.empty();

返回非 null 对象时

Optional < Employee > optional = Optional.of(employee);

返回可 null 对象时,允许 employee 为 null, 但是 optional 的 value 实际是 empty

Optional < Employee > optional = Optional.ofNullable(employee);

Optional.ofNullable 的源代码如下

public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}
  1. 判断Optional 对象的值是否存在

    isPresent() 方法来判断值是否存在,返回值为 boolean 类型,用法如下

    	if (optional.isPresent()) {
    	  System.out.println("Value - " + optional.get()); 
    	 } else {   
    	 System.out.println("Optional is empty"); 
    	 }  
    

    如果想在有值情况下处理一些逻辑,但不想用if 语句判断,可以用方法 ifPresent(Consumer<? super T> consumer) ,接收一个 Consumer 函数

    optional.ifPresent(value -> {   
    	System.out.println("Value present - " + value); 
    });
    
  2. 获取 Optional 对象的值
    get() 方法可以获取值,但是当值为null 时,抛出 NoSuchElementException
    orElse(T) 方法,表示当值为 null 是,指定一个值返回
    orElseGet(Supplier<? extends T> other) ,表示当值为 null 时,执行一个Supplier 函数,示例如下
    java Employee finalEmployee = optional.orElseGet(() -> { return new Employee("0", "Unknown Employee"); });

  3. 当值不存在时抛出异常

@GetMapping("/employees/{id}")
public User getEmployee(@PathVariable("id") String id) {
 return employeeRepository.findById(id).orElseThrow(() -> 
 	new ResourceNotFoundException("Employee not found with id " + id));
}
  1. 过滤值对象的属性

获取一个对象时,我们经常来判断对象的属性值,满足一定条件时执行一些其他业务逻辑,一般实现代码,

if(employee != null && employee.getGender().equalsIgnoreCase("MALE")) { 
   // calling the function 
}

使用optional 的 filter 方法实现同样逻辑:

optional.filter(user -> 
	employee.getGender().equalsIgnoreCase("MALE")) 
	.ifPresent(() -> {   });
  1. 用map 方法抽取转换值

类 Employee 有个获取地址的方法

Address getAddress(){
	return this.address;
}

有个场景:判断员工属于某个国家时,执行一些业务逻辑,一般代码实现

if (employee != null) {
 Address address = employee.getAddress();
 if (address != null && address.getCountry().equalsIgnoreCase("USA")) {
  System.out.println("Employee belongs to USA");
 }
}

利用map方法实现版本

userOptional.map(Employee::getAddress)
.filter(address -> address.getCountry().equalsIgnoreCase("USA")).ifPresent(() -> {
 System.out.println("Employee belongs to USA");
});
  1. 用 flatMap 方法抽取并转换值

如果第6步的例子 getAddress 方法返回值是 Optional < Address >,那 userOptional.map(Employee::getAddress) 的返回值是 Optional< Optional < Address > > , 此时出现了嵌套的 Optional , 可以用方法 flatMap 来解决,

userOptional.flatMap(Employee::getAddress)
.filter(address -> address.getCountry().equalsIgnoreCase("USA")).ifPresent(() -> {
 System.out.println("Employee belongs to USA");
});

总结

使用 java 8 Optional 的优点

  • 有效避免 NullPointerException
  • 不需要写代码判断值为Null
  • 利用Optional 提供的方法可以省去一些模板代码
  • 可以设计出更加简洁优雅的API

注意点

  • 永远不要通过Optional 返回 Null , 这违背了Optional 设计的本意
  • 容器类包括集合、映射、Stream、数组和 Optional, 不应该被包装在Optional 中,直接返回空的容器类,更容易让客户端代码判断
  • 不要用 Optional 类作为集合、数组、键和值的元素,这会给值或键存在的判断增加复杂度
  • 尽量不要将Optional 用作返回值意外的任何用途
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值