最近接手一个项目,里面使用到了Java 8 函数式库 Vavr ,故而来学习一下。
其实很多库都是在java8之前出现的,借鉴了一些新兴语言的特性,比如guava。在java8以后的版本中,提供了很多的函数式编程的特征,已经够用,一些库可以不用了。
Vavr 和 guava提供的功能有一些类似,语法与Scala语言也有一些类似,不知道发明那么多重复的轮子干嘛,关键是还有那么多人用。
不啰嗦了,看一下具体应用。
<dependency>
<groupId>io.vavrgroupId>
<artifactId>vavrartifactId>
<version>0.9.3version>
dependency>
option 主要用来解决空指针问题,并且可以减少一些ifelse的代码。
如果包含较多的if检查,同时带有嵌套语句,那么代码开始变得臃肿。Option通过将null替换为一个有效对象来解决这个问题。使用Option null值会通过None实例来表示,而非null值则是某个具体对象实例。
Option 有两个实现类,一个是None, 一个是Some 。当结果为null时,返回None,否则返回Some包装的对象。
@Test
public void test1() {
Option<Object> noneOption = Option.of(null);
Option<Object> someOption = Option.of("val");
//空被包装成None;非空被包装成Some
Assert.assertEquals("None", noneOption.toString());
Assert.assertEquals("Some(val)", someOption.toString());
//使用Option内部的值需要get
Assert.assertEquals("valss", someOption.get() + "ss");
//Null时可以设置默认值
Assert.assertEquals("baeldung", noneOption.getOrElse("baeldung"));
Assert.assertEquals("val", someOption.getOrElse("baeldung"));
}
Java中没有与元组(Tuple)相对应的结构。Tuple是函数式编程中一种常见的概念。Tuple是一个不可变,并且能够以类型安全的形式保存多个不同类型的对象。Tuple中最多只能有8个元素。一个函数返回多个值时,可以使用元祖。但是要注意返回数据的顺序,倒不如map来的方便
@Test
public void test2() {
Tuple3<String, Integer, Double> java8 = Tuple.of("Java", 8, 1.8);
//获取元祖数据,下标从1开始。语法类似于Scala
String element1 = java8._1;
int element2 = java8._2;
double element3 = java8._3;
Assert.assertEquals("Java", element1);
Assert.assertEquals(8, element2);
Assert.assertEquals(1.8, element3, 0.01);
}
在Vavr, Try是一个容器,来包装一段可能产生异常的代码。这样就不用显式的通过try-catch来处理异常。
@Test
public void test3() {
Try<Integer> result = Try.of(() -> 1 / 0);
//执行结果是否成功
Assert.assertFalse(result.isSuccess());
Assert.assertTrue(result.isFailure());
//获取结果
Integer orElse = result.getOrElse(-1);
Assert.assertEquals(Integer.valueOf(-1), orElse);
//finally
Try<Integer> result1 = Try.of(() -> 1 / 0).andFinallyTry(() -> System.out.println("资源释放"));
//重新包装一个异常抛出
result.getOrElseThrow(() -> new ArithmeticException("除数为0"));
}
Java 8中的函数式接口最多接收两个参数,Vavr对其进行了扩展,最多支持8个参数。
@Test
public void test4() {
IntBinaryOperator intBinaryOperator = (int even, int odd) -> even + odd;
int i = intBinaryOperator.applyAsInt(3, 3);
Assert.assertEquals(6, i);
//jdk默认的Function
Function<Integer, Integer> f = (Integer x) -> x * x;
Integer apply = f.apply(3);
Assert.assertEquals(9, apply.intValue());
//测试4个的
Function4<String, String, String, String, String> concat = (a, b, c, d) -> a + b + c + d;
String finalString = concat.apply("你", "好", "Vavr", "。");
Assert.assertEquals("你好Vavr。", finalString);
}
Java中的集合通常是可变集合,这通常是造成错误的根源。特别是在并发场景下。
在并发场景下大多集合都会会产生问题,因此有了诸如ConcurrentHashMap这样的类。
此外JDK还通过一些其它的方法创建不可变集集合,但误用某些方法时会产生异常。如
下,创建不可修改List,在误调用add的情况下会产生UnsupportedOperationException
异常。
Vavr中的集合则会避免这些问题,并且保证了线程安全、不可变等特性。在Vavr中创建一个list,实例并且不包含那些会导致UnsupportedOperationException异常的方法,且不可变,这样避免误用,造成错误。
@Test
public void test5() {
List<Integer> list1 = List.of(1, 2, 3);
// list1.add(2); // 这里list1是不可变的,add方法没有被隐藏,直接报错
io.vavr.collection.List<Integer> list2 = io.vavr.collection.List.of(1, 2, 3);
io.vavr.collection.List<Integer> list3 = list2.append(4);//list2不会改变,返回值是一个新的list集合
System.out.println(list2); // 输出:List(1, 2, 3)
System.out.println(list3); // 输出:List(1, 2, 3, 4)
//使用一些vavr提供的函数进行计算
int sum = io.vavr.collection.List.of(1, 2, 3).sum().intValue();
Assert.assertEquals(6, sum);
}
不过当需要在多个线程之间共享数据,线程间通信,则还是需要使用线程安全的集合。
Lazy是一个容器,表示一个延迟计算的值。计算被推迟,直到需要时才计算。此外,计算的值被缓存或存储起来,当需要时被返回,而不需要重复计算。
@Test
public void test6(){
Lazy<Double> lazy = Lazy.of(Math::random);
Assert.assertFalse(lazy.isEvaluated()); // 函数并不会被执行
double val1 = lazy.get();
Assert.assertTrue(lazy.isEvaluated());
double val2 = lazy.get();// 读取缓存
Assert.assertEquals(val1, val2, 0.1);
}
在Vavr中,通过Match方法替换switch块。每个条件检查都通过Case方法调用来替换。 $()来替换条件并完成表达式计算得到结果。
@Test
publicvoid test7() {
int input = 2;
String output = API.Match(input).of(
API.Case(API.$(1), "one"),
API.Case(API.$(2), "two"),
API.Case(API.$(3), "three"),
API.Case(API.$(), "?"));
Assert.assertEquals("two", output);
//java switch case
String op = null;
switch (input) {
case 1:
op = "one";
break;
case 2:
op = "two";
break;
case 3:
op = "three";
break;
default:
op = "?";
}
Assert.assertEquals("two", op);
}