目录
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