Vavr入门(2)

发表于2019-05-13,长度2994, 256个单词, 8分钟读完
Flag Counter

前一篇文章介绍了元组和函数式增强,似乎感觉vavr有点鸡肋。希望这篇文章能稍微改善一点你的看法。

本文里的类都是线程安全的。

vavr

Option

这个类看起来像是Java的Optional的重新实现,当然用法不一样。它的返回总是Some或None。

// optional *value*, no more nulls
Option<T> option = Option.of(...);

使用原生Optional下面的代码运行正常:

Optional<String> maybeFoo = Optional.of("foo"); 
then(maybeFoo.get()).isEqualTo("foo");
Optional<String> maybeFooBar = maybeFoo.map(s -> (String)null)  
                                       .map(s -> s.toUpperCase() + "bar");
then(maybeFooBar.isPresent()).isFalse();

而使用Vavr的Option,类似的写法会导致NullPointerException:

Option<String> maybeFoo = Option.of("foo"); 
then(maybeFoo.get()).isEqualTo("foo");
try {
    maybeFoo.map(s -> (String)null) 
            .map(s -> s.toUpperCase() + "bar"); 
    Assert.fail();
} catch (NullPointerException e) {
    // this is clearly not the correct approach
}

这应该是他们之间最大的区别了!

为什么Vavr非要搞出来个空指针呢?这不是违反了它自己的理念吗?

其实Vavr觉得Java自己的Optional才有问题呢!

Vavr认为函数式编程的上下文环境应该是类型不变的:给Some调用map方法返回是Some,所以给None调用map当然返回None了。但是Optional是将非空值映射后得到了空值。

初看起来Option把事情搞得更糟了。但是vavr的目的就是强迫你留意可能会出现null的情况,并相应做出处理,而不是默默的接受。更详细的解释参考《Vavr博客

那该怎么处理null呢?

正确处理null的方法是使用flatMap:

Option<String> maybeFoo = Option.of("foo"); 
then(maybeFoo.get()).isEqualTo("foo");
Option<String> maybeFooBar = maybeFoo.map(s -> (String)null) 
                                     .flatMap(s -> Option.of(s) 
                                                         .map(t -> t.toUpperCase() + "bar"));
then(maybeFooBar.isEmpty()).isTrue();

或者

Option<String> maybeFoo = Option.of("foo"); 
then(maybeFoo.get()).isEqualTo("foo");
Option<String> maybeFooBar = maybeFoo.flatMap(s -> Option.of((String)null)) 
                                     .map(s -> s.toUpperCase() + "bar");
then(maybeFooBar.isEmpty()).isTrue();

Try

如果计算可能导致异常可以使用Try。它和Either有点像,但是语义不同。Try的实例要么是Success,要么是Failure:

// 无需处理异常
Try.of(() -> bunchOfWork()).getOrElse(other);
import static io.vavr.API.*;        // $, Case, Match
import static io.vavr.Predicates.*; // instanceOf

A result = Try.of(this::bunchOfWork)
    .recover(x -> Match(x).of(
        Case($(instanceOf(Exception_1.class)), t -> somethingWithException(t)),
        Case($(instanceOf(Exception_2.class)), t -> somethingWithException(t)),
        Case($(instanceOf(Exception_n.class)), t -> somethingWithException(t))
    ))
    .getOrElse(other);

Either

Either表示两种可能结果中的一种,要么是Left要么是Right。Left只能作用于Left,Right只能作用于Right;交叉调用没有效果。

比如假设compute()方法成功会返回一个整数,失败会返回字符串代码失败原因:

Either<String,Integer> value = compute().right().map(i -> i * 2).toEither();

如果compute()返回Right(1),结果就是Right(2);如果返回Left(“error”)结果还是Left(“error”)。

一般使用Right表示成功。原因嘛,right…

属性检查

属性检查(Property checking, 也叫property testing),是相当彪悍的检查属性的方式。

Vavr的属性检查在io.vavr:vavr-test模块,要先引入它。

Arbitrary<Integer> ints = Arbitrary.integer();

// square(int) >= 0: OK, passed 1000 tests.
Property.def("square(int) >= 0")
        .forAll(ints)
        .suchThat(i -> i * i >= 0)
        .check()
        .assertIsSatisfied();

复杂数据结构的生成器都由简单生成器组成。

Written on May 13, 2019
分类: dev, 标签: java vavr
如果你喜欢,请赞赏! davelet