Vavr入门(2)
前一篇文章介绍了元组和函数式增强,似乎感觉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();
复杂数据结构的生成器都由简单生成器组成。
