vavr使用文档

vavr官方文档

maven依赖

        <!--vavr-->
        <dependency>
            <groupId>io.vavr</groupId>
            <artifactId>vavr</artifactId>
            <version>0.10.4</version>
        </dependency>

Vavr 使用文档

1. Tuples 元组(重点)

参考文档地址1: Vavr - Java8函数式编程拓展库使用指南

参考文档地址2:Java元组Tuple介绍与使用

参考文档地址3: Java Tuple类代码示例

元组是将多个不同类型的数据包裹的一个容器,比如可以将字符串,数字,数组用一个元组包裹,这样即可以通过一个元组变量获取到包括的任一数据

1.1、元组介绍

仅仅一次方法调用就可以返回多个对象,你应该经常需要这样的功能吧.可以return语句只允许返回单个对(可能有人说返回一个集合就可以了,请记住,一个集合也只是一个对象而已)因此,解决办法就是创建一个对象,用它来持有想要返回的对象.当然,可以在每次需要的时候,专门创建一个类来完成这样的工作.可是有了泛型,我们就能够一次性的解决问题,以后再也不用再这种问题上浪费时间了.同时,我们再编译器就可以确保类型安全.

上述概念称为元组(tuple),它是将一组对象直接打包存储与其中的一个单一对象.这个容器对象允许读取其中的元素.但是不允许向其中存放新的对象.(这个概念也称为数据传送对象,或信使)

通常元素具有任意长度,同时,元组中的对象可以是任何不同的类型.不过,我们希望能够为每一个对象指明其类型,并且从容器中读取出来时,能够得到正确的类型.要处理不同长度的问题,我们需要创建不同的元组.采用下面的编码形式无疑是更安全的做法,这样的话,如果程序员想要使用具有不同元素的元组,就强制要求他们创建一个新的元组对象.并且可以利用继承机制实现长度更长的元组.

元组和列表list一样,都可能用于数据存储,包含多个数据;但是和列表不同的是:列表只能存储相同的数据类型,而元组不一样,它可以存储不同的数据类型,比如同时存储int、string、list等,并且可以根据需求无限扩展。

比如说在web应用中,经常会遇到一个问题就是数据分页问题,查询分页需要包含几点信息:当前页数、页大小;查询结果返回数据为:当前页的数据记录,但是如果需要在前台显示当前页、页大小、总页数等信息的时候,就必须有另外一个信息就是:数据记录总数,然后根据上面的信息进行计算得到总页数等信息。这个时候查询某一页信息的时候需要返回两个数据类型,一个是list(当前也的数据记录),一个是int(记录总数)。当然,完全可以在两个方法、两次数据库连接中得到这两个值。事实上在查询list的时候,已经通过sql查询得到总计录数,如果再开一个方法,再做一次数据库连接来查询总计录数,不免有点多此一举、浪费时间、浪费代码、浪费生命。言重了~在这种情况下,我们就可以利用二元组,在一次数据库连接中,得到总计录数、当前页记录,并存储到其中,简单明了!(http://www.cnblogs.com/davidwang456/p/4514659.html)

1.2、元组的示例

二元组常见代码形式可以如下所示:

public class TwoTuple<A, B> {

    public final A first;

    public final B second;

    public TwoTuple(A a, B b){
        first = a;
        second = b;
    }

    public String toString(){
        return "(" + first + ", " + second + ")";
    }

}
//利用继承机制实现长度更长的元组.将上述二元组扩展为三元组代码形式可以如下所示:
public class ThreeTuple<A, B, C> extends TwoTuple<A, B>{

    public final C third;

    public ThreeTuple(A a, B b, C c) {
        super(a, b);
        third = c;
    }

    public String toString(){
        return "(" + first + "," + second + "," + third + ")";
    }

}
//利用继承机制实现长度更长的元组.将上述三元组扩展为四元组代码形式可以如下所示:
public class FourTuple<A, B, C, D> extends ThreeTuple<A,B,C>{

    public final D fourth;

    public FourTuple(A a, B b, C c, D d) {
        super(a, b, c);
        fourth = d;
    }

    public String toString(){
        return "(" + first + "," + second + "," + third + "," + fourth + ")";
    }

}

为了使用元组,你只需定义一个长度适合的元组,将其作为方法的返回值,然后在return语句中创建该元组,并返回即可.例如下面使用方式:

1.3 实际运用

我们使用vavr包下的Tuple,以下为测试代码

import io.vavr.Tuple;
import io.vavr.Tuple2;

/**
 *
 * @author : lyn
 */
public class TestTuples {
    public static void main(String[] args) {
        test1();
    }
    private static void  test1(){
        // 1.1Create a tuple 创建一个元组
        Tuple2<String, Integer> java8 = Tuple.of("Java", 8);
        String str = java8._1;
        Integer num = java8._2;
        // 1.2Map a tuple 改变元组内部元素值
        Tuple2<String, Integer> map = java8.map(s -> s.substring(2) + "vr", c -> c / 8);
        System.out.println(map);
        Tuple2<String, Integer> vavr = java8.map((s, i) -> Tuple.of("vavr", 1));
        System.out.println(vavr);
        //1.3 Transform a tuple 元组转换
        String apply = java8.apply((s, i) -> s.substring(2) + "var" + i / 8);
        System.out.println(apply);
        //(vavr, 1)
        //(vavr, 1)
        //vavar1
    }

}

2. Try(重点)

import org.junit.Test;

import static io.vavr.API.$;
import static io.vavr.API.Case;
import static io.vavr.Predicates.instanceOf;

/**
 * Try.of
 * 描述: 测试代码
 *
 * @author lyn
 * @date 2022/8/9 13:33
 */

public class TryOfTest {
    /**
     * 1 如果有异常则执行getOrElseThrow,抛出指定异常
     */
    @Test
    public void test1() {
        Integer dd = Try.of(() -> {
            return 1 / 0;
        }).getOrElseThrow(() -> new RuntimeException("dd"));
    }

    /**
     * 2 如果有异常则执行getOrElse,返回零
     */
    @Test
    public void test2() {
        Integer dd = Try.of(() -> {
            return 1 / 0;
        }).getOrElse(0);
        System.out.println(dd);
    }

    /**
     * 3 如果有异常则执行先匹配case中的异常,有则执行对应的方法,没有匹配上则执行getOrElse
     */
    @Test
    public void test3() {
        Integer dd = Try.of(() -> {
            return 1 / 0;
        }).recover(x -> API.Match(x).of(
                Case($(instanceOf(NullPointerException.class)), t -> print(t)),
                Case($(instanceOf(ArithmeticException.class)), t -> print(t))
        )).getOrElse(3);
        System.out.println(dd);
    }

    private Integer print(NullPointerException t) {
        System.out.println("空指针异常");
        return null;
    }

    private Integer print(ArithmeticException t) {
        System.out.println("数学异常");
        return null;
    }
}

3 Functions 方法

建议: 对Function函数不了解的先学习 Function接口的使用文档

这里的Functions是对Java8Function做的拓展,其中最大的拓展是Java8中的Function只支持定义一个参数和一个结果,但实际运用当中的话我们得有多个参数,那这样Java8的Function中必须得将参数包装成一个Bean,在Bean中去封装多个参数值,而Vavr则直接将其拓展,使Function可以支持多个参数,最多支持8个参数

4.0 创建示例如下:

    /**
     * functions的三种创建方式
     */
    @Test
    public void test1() {
        // 创建方式一
        Function2<Integer, Integer, Integer> f1 = (a, b) -> a + b;
        // 创建方式二
        Function2<Integer, Integer, Integer> f2 = new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer apply(Integer a, Integer b) {
                return a + b;
            }
        };
        // 创建方式三
        Function2<Integer, Integer, Integer> f3 = Function2.of((a, b) -> a + b);
    }

4.1 Composition 方法组合

  • Composition 方法组合
  • 用法和function的compose/andThen方法类似,都是指定那个规则先执行
  • compose是传入的参数规则先执行,andThen相反
    @Test
    public void test2() {
        Function1<Integer, Integer> func1 = a -> a + 1;
        Function1<Integer, Integer> func2 = a -> a * 2;
        Function1<Integer, Integer> func3 = func1.compose(func2);
        //2*2+1=5
        System.out.println(func3.apply(2));
        //
        System.out.println(func1.andThen(func2).apply(2));
    }

注意: 组合是只有Function1才有的方法,因为Function1继承自Function,而Function中就有compose方法,compose方法是先执行参数里的方法,即func1,将返回值作为func2的参数。

4.2 Lifting 返回值包装

返回值包装即返回Option包装对象

    @Test
    public void test3() {
        Function2<Integer, Integer, Integer> divide = (a, b) -> a / b;
        Function2<Integer, Integer, Option<Integer>> safeDivide = Function2.lift(divide);
        Option<Integer> option = safeDivide.apply(4, 0);
        System.out.println(option);
        //None,没有报错
    }

待补充:返回值用Option包装对象的优点

4.3 Partial application 分部应用

用途: 例如有两个算式 2*5+8 和2*5+4 ,可以利用分布应用,达到对2*5的重复利用
    @Test
    public void test4() {
        Function2<Integer,Integer,Integer> function2=(a,b)->a*b;
        Function1<Integer, Integer> function1 = function2.apply(2);
        System.out.println(function1.apply(4));
        //结果: 8
        //等效于
        Function2<Integer,Integer,Integer> function3=(a,b)->a*b;
        Function1<Integer, Integer> function4 = b->2*b;
        System.out.println(function4.apply(4));
    }

    /**
     * Partial application 分部应用
     * 顺序测试:按传入顺序依次填充
     */
    @Test
    public void test5() {
        Function3<Integer,Integer,Integer,Integer> function=(a,b,c)->a*b+c;
        Function1<Integer, Integer> function1 = function.apply(2, 4);
        System.out.println(function1.apply(3));
        //结果: 11
    }

4.4 Currying 颗粒性

拆分成以个参数的Function去执行,用途待调研

  	@Test
    public void test6() {
        Function3<Integer,Integer,Integer,Integer> sum=(a,b,c)->a+b+c;
        final Function1<Integer,Function1<Integer,Integer>> add2= sum.curried().apply(2);
        System.out.println(add2.apply(3));   // Function3
        System.out.println(add2.apply(3).apply(4));
    }

4.5 Memoization 内存存储

节约内存

    @Test
    public void test7() {
        Function0<Double> hashCache =
                Function0.of(Math::random).memoized();
        double randomValue1 = hashCache.apply();
        double randomValue2 = hashCache.apply();
        //这里两个输出的值一致,randomValue1时产生之后就已缓存,第二次调用直接获取缓存值
        System.out.println(randomValue1);
        System.out.println(randomValue2);
        Function2<Integer,Integer,Integer> sum=(a, b) -> a + b;
        Function2<Integer, Integer, Integer> function2 = sum.memoized();
        Integer num1 = function2.apply(128, 136);
        Integer num2 = function2.apply(128, 136);
        System.out.println(num1==num2);
        //true
        Integer num3 = sum.apply(128, 136);
        Integer num4 = sum.apply(128, 136);
        System.out.println(num3==num4);
        //false

    }

4 API.Match (switch)

在Vavr中,通过Match方法替换switch块。

import io.vavr.API;
import org.junit.Test;
/**
 * 在Vavr中,通过Match方法替换switch块。
 * 每个条件检查都通过Case方法调用来替换。
 * $()来替换条件并完成表达式计算得到结果。
 *
 * @author : lyn
 * 技术点 :
 * @date : 2022-08-09 21:53
 */
public class MatchTest {
    /**
     * 等同于switch语句,最后一个相当于default
     * 1 用途:返回vo时将数字处理成对应的汉字
     */
    @Test
    public void test1() {
        int num = 3;
        String result = API.Match(num).of(
                API.Case(API.$(1), "one"),
                API.Case(API.$(2), "two"),
                API.Case(API.$(3), "three"),
                API.Case(API.$(), "其他")
        );
        System.out.println(result);

        //等同于java的switch语句
        String op = null;
        switch (num) {
            case 1:
                op = "one";
                break;
            case 2:
                op = "two";
                break;
            case 3:
                op = "three";
                break;
            default:
                op = "其他";
        }
        System.out.println(op);
    }

    /**
     * 类似switch 执行不同的逻辑,可能太过繁琐,需要定义很多方法
     * 优点: 清晰
     */
    @Test
    public void test2() {
        int num = 3;
        String result = API.Match(num).of(
                API.Case(API.$(1), t -> method1(t)),
                API.Case(API.$(2), t -> method2()),
                API.Case(API.$(3), method3()),
                API.Case(API.$(), method())
        );

    }

    private String method() {
        //逻辑其他
        return null;
    }

    private String method1(Integer num) {
        //逻辑 1
        return null;
    }

    private String method2() {
        //逻辑 2
        return null;
    }

    private String method3() {
        //逻辑 3
        return null;
    }

    /**
     * 3 类似使用switch执行不同的逻辑,避免了定义很多方法
     */
    @Test
    public void test3() {
        int num = 3;
        String result = API.Match(num).of(
                API.Case(API.$(1), () -> {
                    System.out.println("ijij");
                    //必须有返回值
                    return "";
                }),
                API.Case(API.$(2), t -> {
                    System.out.println(t);
                    return "two";
                }),
                API.Case(API.$(3), () -> {
                    throw new RuntimeException("抛出异常");
                }),
                API.Case(API.$(), method())
        );

    }
}

5 其他

用处极小

5.1 Lazy 惰性加载

Lazy表示一个延迟计算的值,计算会被推迟,直到需要时才计算。此外,计算的值会被缓存或存储起来,当需要时被返回,而不需要重新计算,具体如下:

import io.vavr.Lazy;
import org.junit.Test;

/**
 * Lazy 惰性加载
 *
 * Lazy表示一个延迟计算的值,计算会被推迟,直到需要时才计算。
 * 此外,计算的值会被缓存或存储起来,当需要时被返回,而不需要重新计算
 * @author : lyn
 * @date : 2022-08-15 21:19
 */
@SuppressWarnings("all")
public class LazyTest {
    @Test
    public void test1() {
        Lazy<Double> lazy = Lazy.of(Math::random);
        System.out.println(lazy.isEvaluated());  // = false
        lazy.get();         // = 0.123 (random generated)
        System.out.println(lazy.isEvaluated()); // = true
        lazy.get();         // = 0.123 (memoized)
    }

}

5.2 Either 两者之一

/**
 * Either 两者之一
 * 有什么用???
 *
 * @author : lyn
 * @date : 2022-08-15 21:19
 */
@SuppressWarnings("all")
public class EitherTest {
    @Test
    public void test1() {
        Function<Integer, Either<Integer, Integer>> function =
                i -> i > 0 ? Either.right(i) : Either.left(-100);
        Either<Integer, Integer> either = function.apply(100);
        System.out.println(either.map(r -> r * 100).get());

        //等效于
        Integer num = new Random().nextInt(10)-5;
        System.out.println(num);
       if (num<=0){
           num=-100;
       }
        System.out.println(num * 100);

    }

}

更多见参考文档地址1: Vavr - Java8函数式编程拓展库使用指南

code地址: https://gitee.com/li-yanning/concise-code

posted @ 2022-08-15 22:12  进击的小蔡鸟  阅读(1291)  评论(0编辑  收藏  举报