Java 8 函数式库 Vavr 学习笔记

概述

最近接手一个项目,里面使用到了Java 8 函数式库 Vavr ,故而来学习一下。

其实很多库都是在java8之前出现的,借鉴了一些新兴语言的特性,比如guava。在java8以后的版本中,提供了很多的函数式编程的特征,已经够用,一些库可以不用了。

Vavr 和 guava提供的功能有一些类似,语法与Scala语言也有一些类似,不知道发明那么多重复的轮子干嘛,关键是还有那么多人用。

不啰嗦了,看一下具体应用。

引入依赖

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

option

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

元组Tuple

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

Try

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

集合Collections

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

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

模式匹配Pattern Matching

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

参考链接

发布了234 篇原创文章 · 获赞 227 · 访问量 95万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: Age of Ai 设计师: meimeiellie

分享到微信朋友圈

×

扫一扫,手机浏览