您现在的位置是:亿华云 > 域名
Vavr Option:Java Optional 的另一个选项
亿华云2025-10-03 06:38:51【域名】4人已围观
简介每当涉及Java,总会有很多选项。 这篇文章讨论了 Java 基础类 Optional 用法,与 Vavr 中的对应方法进行比较。Java 8最早引入了 Opti
每当涉及Java,选项总会有很多选项。选项 这篇文章讨论了 Java 基础类 Optional 用法,选项与 Vavr 中的选项对应方法进行比较。Java 8最早引入了 Optional,选项把它定义为“一种容器对象,选项可以存储 null 或非 null 值”。选项
通常,选项在返回值可能为null的选项地方,会出现NullPointerException。选项开发人员可以使用 Optional 避免 null 值检查。选项在这种情况下,选项Optional 提供了一些方便的选项功能。但可惜的选项是,Java 8并没有包含所有功能。选项Optional中的某些功能需要使用 Java 11。要解决这类问题还可以使用 Vavr Option类。
本文将介绍如何使用 Java Optional类,并与 Vavr Option 进行比较。注意:示例代码要求使用Java 11及更高版本。所有代码在 Vavr0.10.2环境下完成测试。
让我们开始吧。
Java Optional 简介
Optional 并不是什么新概念,像 Haskell、Scala 这样的函数式编程语言已经提供了实现。调用方法后,亿华云计算返回值未知或者不存在(比如 null)的情况下,用 Optional 处理非常好用。下面通过实例进行介绍。
新建 Optional 实例
首先,需要获得 Optional 实例,有以下几种方法可以新建 Optional 实例。不仅如此,还可以创建empty Optional。方法一,通过 value 创建,过程非常简单:
Optional<Integer> four = Optional.of(Integer.valueOf( 4));
if(four.isPresent){
System.out.println( "Hoorayy! We have a value");
} else{
System.out.println( "No value");
}为Integer 4 新建一个Optional实例。这种方法得到的 Optional 始终包含一个 value 且不为 null,例如上面这个示例。使用 ifPresent() 可以检查value是否存在。可以注意到 four 不是 Integer,而是一个装有整数的容器。如果确认 value 存在,可以用 get() 方法执行拆箱操作。具有讽刺意味的是,调用 get() 前如果不进行检查,可能会抛出 NoSuchElementException。
方法二,得到 Optional 对象的另一种方法是使用 stream。云服务器提供商Stream提供的一些方法会返回Optional,可以用来检查结果是否存在,例如:
findAny
findFirst
max
min
reduce
查看下面的代码段:
Optional<Car> car = cars.stream().filter(car->car.getId().equalsIgnoreCase(id)).findFirst();方法三,使用 Nullable 新建 Optional。可能产生 null:
Optional<Integer> nullable = Optional.ofNullable(client.getRequestData());最后,可以新建一个 empty Optional:
Optional<Integer> nothing = Optional.empty();如何使用 Optional
获得 Optional 对象后即可使用。一种典型的场景是在 Spring 仓库中根据 Id 查找记录。可以使用 Optional 实现代码逻辑,避免 null 检查(顺便提一下,Spring 也支持 Vavr Option)。比如,从图书仓库里查找一本书。
Optional<Book> book = repository.findOne("some id");首先,如果有这本书,可以继续执行对应的业务逻辑。在前面的章节用 if-else实现了功能。当然,还有其他办法:Optional 提供了一个方法,接收 Consumer 对象作为输入:
repository.findOne("some id").ifPresent(book -> System.out.println(book));还可以直接使用方法引用,看起来更简单:
repository.findOne("some id").ifPresent(System.out::println);如果仓库中没有该书,可以用ifPresentOrElseGet提供回调函数:
repository.findOne( "some id").ifPresentOrElseGet(book->{
// 如果 value 存在}, ()->{
// 如果 value 不存在});如果结果不存在,可以返回另一个value:
Book result = repository.findOne("some id").orElse(defaultBook);但是,Optional 也有缺点,使用时需要注意。最后一个例子中,亿华云“确保”无论如何都能获得一本书,可能在仓库中,也可能来自 orElse。但如果默认的返回值不是常量或者需要支持一些复杂方法该怎么办?首先,Java 无论如何都会执行 findOne,然后调用 orElse方法。默认返回值可以为常量,但正如我之前所说那样,执行过程比较耗时。
另一个示例
下面用一个简单的示例介绍如何实际使用 Optional 和 Option 类。有一个 CarRepository,可以根据提供的 ID(比如车牌号)查找汽车,接下来用这个示例介绍如何使用 Optional 和 Option。
首先,加入下面代码
从 POJO 类 Car 开始。它遵循 immutable 模式,所有字段都标记为 final,只包含 getter 没有 setter。初始化时提供所有数据:
public classCar {
privatefinal String name;
privatefinal String id;
privatefinal String color;
public Car (String name, String id, String color){
this.name = name;
this.id = id;
this.color = color;
}
public String getId (){
returnid;
}
public String getColor (){
returncolor;
}
public String getName (){
returnname;
}
@Override
public String toString (){
return "Car "+name+ " with license id "+id+ " and of color "+color;
}
}接下来创建 CarRepository类。提供两种方法根据Id查找汽车:一种是老办法,使用 Optional。和之前在 Spring 仓库的做法类似,结果可能为 null。
public classCarRepository {
privateList
{
getSomeCars();
}
Car findCarById (String id){
for (Car car: cars){ function(){ //外汇跟单 if(car.getId().equalsIgnoreCase(id)){
returncar;
}
}
returnnull;
}
Optional
cars.stream().filter(car->car.getId().equalsIgnoreCase(id)).findFirst();
}
private void getSomeCars (){
cars = newArrayList<>();
cars.add( new Car( "tesla" , "1A9 4321" , "red"));
cars.add( new Car( "volkswagen" , "2B1 1292" , "blue"));
cars.add( new Car( "skoda" , "5C9 9984" , "green"));
cars.add( new Car( "audi" , "8E4 4321" , "silver"));
cars.add( new Car( "mercedes" , "3B4 5555" , "black"));
cars.add( new Car( "seat" , "6U5 3123" , "white"));
}
}注意:初始化过程会在仓库中添加一些汽车模拟数据,便于演示。为了突出重点,避免问题复杂化,下面的讨论专注于 Optional 和 Option。
使用Java Optional
使用JUnit创建一个新测试:
@Test
void getCarById (){
Car car = repository.findCarById( "1A9 4321");
Assertions.assertNotNull(car);
Car nullCar = repository.findCarById( "M 432 KT");
Assertions.assertThrows(NullPointerException. class, ()->{
if(nullCar == null){
throw newNullPointerException();
}
});
}上面的代码段采用了之前的老办法。查找捷克牌照 1A9 4321对应的汽车,检查该车是否存在。输入俄罗斯车牌找不到对应的汽车,因为仓库中只有捷克车。结果为 null 可能会抛出 NullPointerException。
接下来用Java Optional。第一步,获得 Optional 实例,从存储库中使用指定方法返回 Optional:
@Test
void getCarByIdWithOptional (){
Optional<Car> tesla = repository.findCarByIdWithOptional( "1A9 4321");
tesla.ifPresent(System.out::println);
}这时调用findCarByIdWithOptional方法打印车辆信息(如果有的话)。运行程序,得到以下结果:
Car tesla with license id 1A9 4321 and of color red但是,如果代码中没有特定方法该怎么办?这种情况可以从方法返回可能包含 null 的 Optional,称为nullable。
Optional<Car> nothing = Optional.ofNullable(repository.findCarById( "5T1 0965"));
Assertions.assertThrows(NoSuchElementException. class, ()->{
Car car = nothing.orElseThrow(()-> newNoSuchElementException());
});上面这段代码段中,我们发现了另一种方法。通过 findCarById 创建 Optional,如果未找到汽车可以返回 null。没有找到车牌号 5T1 0965汽车时,可以用 orElseThrow 手动抛出 NoSuchElementException。另一种情况,如果请求的数据不在仓库中,可以用orElse返回默认值:
Car audi = repository.findCarByIdWithOptional( "8E4 4311")
.orElse( new Car( "audi", "1W3 4212", "yellow"));
if (audi.getColor().equalsIgnoreCase( "silver")){
System.out.println( "We have silver audi in garage!");
} else{
System.out.println( "Sorry, there is no silver audi, but we called you a taxi");
}好的,车库里没有找到银色奥迪,只好打车了!
使用 Vavr Option
Vavr OptionOption提供了另一种解决办法。首先,在项目中添加依赖,(使用 Maven)安装 Vavr:
vavr
<version> 0.10.2</dependency>简而言之,Vavr 提供了类似的 API 新建 Option 实例。可以从 nullable 新建 Option 实例,像下面这样:
Option<Car> nothing = Option.of(repository.findCarById("T 543 KK"));也可以用 none 静态方法创建一个empty容器:
Option<Car> nullable = Option.none();此外,还有一种方法可以用 Java Optional 新建 Option。看下面这段代码:
Option<Car> result = Option.ofOptional(repository.findCarByIdWithOptional("5C9 9984"));使用 Vavr Option,可以使用与 Optional相同的 API 来完成上述任务。例如,设置默认值:
Option<Car> result = Option.ofOptional(repository.findCarByIdWithOptional( "5C9 9984"));
Car skoda = result.getOrElse( new Car( "skoda", "5E2 4232", "pink"));
System.out.println(skoda);或者,请求的数据不存在时可以抛出异常:
Option
, ()->{
nullable.getOrElseThrow(()-> newNoSuchElementException());
});另外,当数据不可用时,可以执行以下操作:
nullable.onEmpty(()->{
///runnable});如何根据数据是否存在来执行相应操作,类似 Optional 中 ifPresent?有几种实现方式。与 Optional 中 isPresent 类似,在 Option 中对应的方法称为 isDefined:
if(result.isDefined()){
// 实现功能}然而,使用 Option能摆脱 if-else。 是否可以用Optional相同的方式完成? 使用 peek 操作:
result.peek(val -> System.out.println(val)).onEmpty(() -> System.out.println("Result is missed"));此外,Vavr Option还提供了一些其他非常有用的方法,在函数式编程上比Optional类效果更好。 因此,建议您花一些时间来探索 Vavr Option javadocs尝试使用这些API。 我会持续跟进一些类似 map、narrow、isLazy 和 when 这样有趣的功能。
另外,Option只是 Vavr 开发库的一部分,其中还包含了许多其他关联类。 不考虑这些类直接与 Optional 比较是不对的。 接下来我会继续编写 Vavr 主题的系列文章,介绍 Vavr 相关技术例如 Try、Collections 和 Streams。 敬请关注!
总结
本文中,我们讨论了 Java Optional 类。 Optional 并不是什么新概念,像 Haskell、Scala这样的函数式编程语言已经提供了实现。 调用方法后,返回值未知或者不存在(比如 null)的情况下,Optional 非常有用。 然后,介绍了 Optional API,并设计了一个汽车搜索示例进行说明。 最后,介绍了 Optional 的另一种替代方案 Vavr Option 并通过示例进行了介绍。
很赞哦!(5192)