当前位置: 首页 > 工具软件 > Vavr > 使用案例 >

Java Optional&Vavr Option:基本介绍

闾丘冠玉
2023-12-01

嗨,您好! 我今天想讨论一些基本的Java主题-Optional类的用法,并将其与Vavr库中的替代方法进行比较。 可选的方法最初是在Java 8中引入的,并且被定义为“可能包含也可能不包含非空值值的容器对象”。 开发人员利用Optionals以避免执行代码时对位置进行空值检查,这不是结果,而是空值价值,它可以导致空指针异常。 在这种情况下,Optional为我们提供了一些精美的功能,但并非所有功能都在第8版中引入,某些功能需要Java11。处理这些问题的另一种方法是Vavr的Option类。

在本文中,我们将学习如何使用Java Optional类,然后将其与Vavr Option类进行比较。 请注意,该代码至少需要Java 11并已通过Vavr进行了测试0。10。2。

Introducing Java Optional

Optional的概念并不是什么新鲜事物,并且已经在功能编程语言(例如Haskell或Scala)中实现。 在对方法调用可能返回未知值或不存在的值(例如null)的情况进行建模时,它被证明非常有用。 让我们看看如何处理它。

Creation of 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()方法。 您可以注意到,四不是整数,而是容器,在里面保存整数。 当我们确定该值在里面时,我们可以使用得到()方法。 具有讽刺意味的是,如果我们使用得到()无需检查,我们可以以NoSuchElementException结尾。

Another way to obtain an Optional is using streams. Several terminal stream's methods return Optionals, so we can manipulate them and checking existence or absence, for example:

  • findAnyfindFirst最高分减少

检查以下代码片段:

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

下一个选项是从代码创建Optional,可能产生空值,例如 来自Nullable:

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

最后,我们可以创建一个空的可选的:

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

How to use Optional

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

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

首先,我们可以执行一些逻辑,以防万一,被表达。 我们在上一节中使用if-else做到了这一点,但是我们不需要:Optional为我们提供了一种接受消费者与对象:

repository.findOne("some id").ifPresent(book -> {});

甚至更简单,我们可以使用方法引用来编写它:

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

如果存储库中没有书籍,则可以使用isPresent OrElseGet方法:

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

另外,如果没有给出运算结果,我们可以得到另一个值:

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

但是,使用Optionals,我们需要记住可能存在的缺点。 在最后一个示例中,我们自己“保证”我们无论如何都会得到一本书,无论是呈现在底层存储库中还是来自要不然。 但是,如果默认值不是不变,还需要一些复杂的方法吗? 首先,Java无论如何评估找一个。 然后,它必须处理要不然 method. Yes, if it is just a default 不变 value it is ok, but it can be as I said before time-consuming as well.

Let have an example

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

Add some code first

从POJO类开始汽车。 它遵循一成不变的模式,因此所有字段都是最终字段,我们只有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;
    }
}

第二件事是创建汽车储存库类。 按ID查找汽车将有两个选择-使用可能产生空结果的旧方法以及使用诸如Spring存储库的Optional。

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

注意,在初始化过程中,我们还会用一些模拟汽车填充存储库,因此我们没有任何底层数据库。 我们需要避免复杂性,让我们专注于Optional和Option,而不是存储库。

Finding cars with Java Optional

Create a new test with JUnit (I also have an excellent tutorial on JUnit 5 if you don't have experience with it).

@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并检查它是否存在。 然后我们发现一辆缺少的车,因为它有俄罗斯的车牌,而我们的仓库里只有捷克的车。 它是空值,因此可能会导致NullPointerException。

接下来,让我们转到Java Optionals。 第一步是使用返回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

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

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

在此代码段中,我们使用另一种方式。 我们从创建可选findCarById可以返回空值如果找不到车。 我们手动使用否则带有牌照的所需汽车时抛出NoSuchElementException的方法5T1 0965存在。 另一种情况是使用要不然带有一些默认值,如果请求的数据在存储库中不可用:

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

好的,我们的车库里没有银色的奥迪,所以我们必须叫出租车!

Finding cars with 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 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<Car> nullable = Option.none();
Assertions.assertThrows(NoSuchElementException.class, ()->{
    nullable.getOrElseThrow(()->new NoSuchElementException());
});

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

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

如果我们需要根据数据的存在执行操作,该怎么办,就像我们对Optional所做的那样ifPresent? 我们可以通过几种方式做到这一点。 有一个平等的方法存在在Optional中调用被定义为:

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

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

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

Also, there are some other very useful methods in Vavr Option that gonna make your code much more functional, than with built-in Optional class. So I encourage you to take some time and explore Vavr Option javadocs and experiment with these APIs. I can note some cool things like map, narrow, isLazy and when that you definetly need to check out. Also, Vavr Option is a part of Vavr family and is heavily integrated with Vavr other classes, and make such comparasion with Optional in vaccuum, e.g. in abscence of such classes is not correct. So, I would also write posts on other Vavr topics, like Try, Collections, Streams. Don't forget to sign up for updates!

Conclusion

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

Read more

  • José Paumard. Optionals: Patterns and Good Practices (2016) Oracle Community Directory, read here
  • Mervyn McCreight and Mehmet Emin Tok. A look at the Optional datatype in Java and some anti-patterns when using it (2019) FreeCodeCamp, read here
  • Radosław Nowak. Functional programming in Java with Vavr (2018) Pragmatists, read here

from: https://dev.to//andreevich/java-optional-vavr-option-an-essential-introduction-10cb

 类似资料: