四时宝库

程序员的知识宝库

虽然了解Java Optional 但却不清楚 Vavr Option?


讨论一个重要的Java主题-Optional类的用法-并将其与Vavr库中的替代方法进行比较。可选最初在Java 8中引入,并定义为“可能包含也可能不包含非null值的容器对象”。开发人员利用Optionals来避免在代NullPointerException。在这种情况下,Optional为我们提供了一些精美的功能,但并非所有功能都在第8版中引入,某些功能需要Java11。另一种解决这些问题的方法是使用Vavr的Option类。在本文中,我们将学习如何使用Java的Optional类,然后将其与Vavr的Option类进行比较。注意:此代码需要Java 11 +



Java Optional 简介

已经以功能性编程语言(如Haskell或Scala)实现了。在对方法调用可能返回未知值或不存在的值(例如null)的情况进行建模时,它被证明非常有用。

创建 Optional

首先,我们需要创建Optional的实例。有几种方法可以实现。我们还可以创建一个空的Optional。看看第一种方法,这非常简单:

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");
}

我们从4的Integer值构建一个Optional,这意味着始终应该有一个值并且它不能为null,但这只是一个例子。我们使用ifPresent()方法检查值是否存在。您可以注意到四个不是整数;它是一个容器,里面装有整数。当我们确定该值在内部时,可以使用get()获取值。如果我们不进行检查就使用get(),则可能NoSuchElementException结尾。获得可选参数的另一种方法是使用流。几种终端流的方法返回Optional,因此我们可以对其进行操作并检查其存在与否,例如:

  • findAny
  • findFirst
  • max
  • min
  • reduce

代码片段

Optional<Car> car = cars.stream().filter(car->car.getId().equalsIgnoreCase(id)).findFirst();

创建 Nullable:

Optional<Integer> nullable = Optional.ofNullable(client.getRequestData());

创建 空 Optional:

Optional<Integer> nothing = Optional.empty();

使用 Optional

一种最普遍的情况是在Spring存储库中使用它来通过ID查找一条记录,因此我们可以在Optional上构建逻辑并避免空检查(顺便说一句,Spring还支持Vavr Options)。假设我们有一个图书库,想找到一本书。

Optional<Book> book = repository.findOne("some id");


首先,如果有这本书,我们可以执行一些逻辑。我们在上一节中使用if-else做到了这一点,但是我们不需要:可选为我们提供了一种接受对象的消费者的方法:

repository.findOne("some id").ifPresent(book -> System.out.println(book));

或者,我们可以使其更简单。我们可以使用方法引用:

repository.findOne("some id").ifPresent(System.out::println);

如果存储库中没有书籍,则可以使用theifPresentOrElseGet方法提供替代回调

repository.findOne("some id").ifPresentOrElseGet(book->{
// if value is presented
}, ()->{
// if value is absent
});

另外,如果未显示运算结果,我们可以设置默认值:

Book result = repository.findOne("some id").orElse(defaultBook);

但是,使用Optionals,我们需要记住可能的缺点。在最后一个示例中,我们“保证”自己无论如何都能获得一本书。它显示存储或来自orElse。但是,如果此默认值不是恒定的,但又需要一些复杂的方法怎么办?首先,Java无论如何都会评估findOne。然后,它必须处理orElse方法。是的,如果它只是一个默认常数,就可以了。

另一个实例

让我们创建一个简单的示例来检查如何实际使用Optional和Option类。我们将有一个CarRepository,可以根据提供的ID(例如在车牌号上)找到一辆汽车。然后,我们将看到如何操作Optional和Options。

首先,添加基础代码

它遵循不可变模式,因此所有字段都是final,我们只有getter而没有setter。初始化期间将提供所有数据。

public class Car {

    private final String name;
    private final String id;
    private final String color;

    public Car (String name, String id, String color){
        this.name = name;
        this.id = id;
        this.color = color;
    }

    public String getId(){
        return id;
    }

    public String getColor() {
        return color;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Car "+name+" with license id "+id+" and of color "+color;
    }
}

The second thing is to create the CarRepository class. It requires two options for finding car by Id — using the old way with a possible null result and using Optional, as we do in Spring repositories.

第二,是创建CarRepository类。findCarById 可能返回null.

public class CarRepository {

    private List<Car> cars;

    public CarRepository(){
       getSomeCars();
    }

    Car findCarById(String id){
        for (Car car: cars){
            if (car.getId().equalsIgnoreCase(id)){
                return car;
            }
        }
        return null;
    }

    Optional<Car> findCarByIdWithOptional(String id){
        return cars.stream().filter(car->car.getId().equalsIgnoreCase(id)).findFirst();
    }

    private void getSomeCars(){
        cars = new ArrayList<>();
        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"));
    }
}


Java Optional 查询Car

测试代码片段

@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 new NullPointerException();
        }
    });
}

上面的代码段演示了旧方法。我们找到了一辆带有车牌1A9 4321的汽车,并检查了该汽车的存在。

@Test
void getCarByIdWithOptional(){
    Optional<Car> tesla = repository.findCarByIdWithOptional("1A9 4321");
    tesla.ifPresent(System.out::println);
}

在这种情况下,我们使用findCarByIdWithOptional方法,然后打印Car(如果有的话)。如果运行它,将得到以下输出:

Car tesla with license id 1A9 4321 and of color red


但是,如果我们的代码中没有该特殊方法,该怎么办?在这种情况下,我们可以从可能返回空值的方法中获取Optional。它被称为可空的。

Optional<Car> nothing = Optional.ofNullable(repository.findCarById("5T1 0965"));
Assertions.assertThrows(NoSuchElementException.class, ()->{
    Car car = nothing.orElseThrow(()->new NoSuchElementException());
});


在此代码段中,我们找到了另一种方法。我们从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 Option是处理这些任务的另一种方法。首先,使用依赖项管理在您的项目中安装Vavr(我使用Maven):

<dependency>
   <groupId>io.vavr</groupId>
   <artifactId>vavr</artifactId>
   <version>0.10.2</version>
</dependency>

简而言之,Vavr具有类似的API可创建Option实例。我们可以从nullable创建Option,如下所示:

Option<Car> nothing = Option.of(repository.findCarById("T 543 KK"));

或者我们可以使用一个静态方法创建一个空容器:

Option<Car> nullable = Option.none();

此外,还有一种方法可以从Java创建Option(可选)!看一下下面的代码片段:

Option<Car> result = Option.ofOptional(repository.findCarByIdWithOptional("5C9 9984"));


借助Vavr选项,我们可以使用与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<Car> nullable = Option.none();
Assertions.assertThrows(NoSuchElementException.class, ()->{
nullable.getOrElseThrow(()->new NoSuchElementException());
});

或者,当数据不可用时,我们可以执行以下操作

nullable.onEmpty(()->{
///runnable
});

如果我们需要根据数据的存在来执行操作,就像我们使用Optional的ifPresent一样,该怎么办?我们可以通过几种方式做到这一点。在Option中有一个相等的方法isPresent,在Option中被称为isDefined:

if (result.isDefined()){
// do something
}


但是,我们使用Option来摆脱if-else构造。我们可以以与Optional相同的方式浮动吗?我们可以通过窥视的存在来执行操作:

result.peek(val -> System.out.println(val)).onEmpty(() -> System.out.println("Result is missed"));


此外,Vavr Option中还有其他一些非常有用的方法,这些方法可以使您的代码比内置的Optional类具有更多功能。因此,我鼓励您花一些时间来探索Vavr Option Javadocs并尝试使用这些API。

总结



在本文中,我们讨论了Java中的Optional类。可选的概念并不是什么新鲜事物,已经在其他功能编程语言(例如Haskell和Scala)中实现了。在对方法调用可能返回未知值或不存在的值(例如null)的情况进行建模时,它被证明非常有用。然后,我们探索了它的API,并创建了一些示例,这些示例使用可选逻辑查找汽车并处理结果。最后,我们找到了Optional-Vavr的Option的替代方法并描述了其方法。

希望你喜欢!在评论中留下想法或问题。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接