Java 8+ 函数式库Vavr功能简介

1 概述

Vavr 是Java 8+中一个函数式库,提供了一些不可变数据类型及函数式控制结构。

1.1 Maven 依赖

添加依赖,可以到maven仓库中查看最新版本。

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

2. Option

Option的作用是消除代码中的null检查。在Vavr中Option是一个对象容器,与Optional类似,有一个最终结果。 Vavr中的Option实现了Serializable, Iterable接口,并且具有更加丰富的API。在Java中,我们通常通过if语句来检查引用是否为null,以此来保证系统健壮与稳定。如果不检查会出现空指针异常。

@Test
public void givenValue_whenNullCheckNeeded_thenCorrect() {Object object = null;if (object == null) {object = "someDefaultValue";}assertNotNull(possibleNullObj);
}

如果包含较多的if检查,同时带有嵌套语句,那么代码开始变得臃肿。Option通过将null替换为一个有效对象来解决这个问题。使用Option null值会通过None实例来表示,而非null值则是某个具体对象实例。

@Test
public void givenValue_whenCreatesOption_thenCorrect() {Option<Object> noneOption = Option.of(null);Option<Object> someOption = Option.of("val");assertEquals("None", noneOption.toString());assertEquals("Some(val)", someOption.toString());
}

代码中调用toString时,并没有进行检查来处理NullPointerException问题。Option的toString会返回给我们一个有意义的值,这里是 “None”。当值为null时,还可以指定默认值。

@Test
public void givenNull_whenCreatesOption_thenCorrect() {String name = null;Option<String> nameOption = Option.of(name);assertEquals("baeldung", nameOption.getOrElse("baeldung"));
}

当为非null时返回值本身。

@Test
public void givenNonNull_whenCreatesOption_thenCorrect() {String name = "baeldung";Option<String> nameOption = Option.of(name);assertEquals("baeldung", nameOption.getOrElse("notbaeldung"));
}

这样在处理null相关检查时,只需要写一行代码即可。

3. 元组Tuple

Java中没有与元组(Tuple)相对应的结构。Tuple是函数式编程中一种常见的概念。Tuple是一个不可变,并且能够以类型安全的形式保存多个不同类型的对象。Tuple中最多只能有8个元素。

public void whenCreatesTuple_thenCorrect1() {Tuple2<String, Integer> java8 = Tuple.of("Java", 8);String element1 = java8._1;int element2 = java8._2();assertEquals("Java", element1);assertEquals(8, element2);
}

引用元素时从1开始,而不是0。

Tuple中的元素必须是所声明的类型。

@Test
public void whenCreatesTuple_thenCorrect2() {Tuple3<String, Integer, Double> java8 = Tuple.of("Java", 8, 1.8);String element1 = java8._1;int element2 = java8._2();double element3 = java8._3();assertEquals("Java", element1);assertEquals(8, element2);assertEquals(1.8, element3, 0.1);
}

当需要返回多个对象时可以考虑使用Tuple。

4. Try

在Vavr, Try是一个容器,来包装一段可能产生异常的代码。Option用来包装可能产生null的对象,而Try用来包装可能产生异常的代码块,这样就不用显式的通过try-catch来处理异常。下面的代码用来检查是否产生了异常。

@Test
public void givenBadCode_whenTryHandles_thenCorrect() {Try<Integer> result = Try.of(() -> 1 / 0);assertTrue(result.isFailure());
}

我们也可以在产生异常时获取一个默认值。

@Test
public void givenBadCode_whenTryHandles_thenCorrect2() {Try<Integer> computation = Try.of(() -> 1 / 0);int errorSentinel = result.getOrElse(-1);assertEquals(-1, errorSentinel);
}

或者根据具体需求再抛出一个异常。

@Test(expected = ArithmeticException.class)
public void givenBadCode_whenTryHandles_thenCorrect3() {Try<Integer> result = Try.of(() -> 1 / 0);result.getOrElseThrow(ArithmeticException::new);
}

5. 函数式接口

Java 8中的函数式接口最多接收两个参数,Vavr对其进行了扩展,最多支持8个参数。

@Test
public void whenCreatesFunction_thenCorrect5() {Function5<String, String, String, String, String, String> concat =(a, b, c, d, e) -> a + b + c + d + e;String finalString = concat.apply("Hello ", "world", "! ", "Learn ", "Vavr");assertEquals("Hello world! Learn Vavr", finalString);
}

此外可以通过静态工厂方法FunctionN.of使用方法引用来创建一个Vavr函数。

public int sum(int a, int b) {return a + b;
}
@Test
public void whenCreatesFunctionFromMethodRef_thenCorrect() {Function2<Integer, Integer, Integer> sum = Function2.of(this::sum);int summed = sum.apply(5, 6);assertEquals(11, summed);
}

6. 集合Collections

Java中的集合通常是可变集合,这通常是造成错误的根源。特别是在并发场景下。
此外Jdk中的集合类存在一些不足。例如JDK中的集合接口提供的一个方法clear,
该方法删除所有元素而且没有返回值。

interface Collection<E> {void clear();
}

在并发场景下大多集合都会会产生问题,因此有了诸如ConcurrentHashMap这样的类。
此外JDK还通过一些其它的方法创建不可变集集合,但误用某些方法时会产生异常。如
下,创建不可修改List,在误调用add的情况下会产生UnsupportedOperationException
异常。

@Test(expected = UnsupportedOperationException.class)
public void whenImmutableCollectionThrows_thenCorrect() {java.util.List<String> wordList = Arrays.asList("abracadabra");java.util.List<String> list = Collections.unmodifiableList(wordList);list.add("boom");
}

Vavr中的集合则会避免这些问题,并且保证了线程安全、不可变等特性。在Vavr中创建一个list,实例并且不包含那些会导致UnsupportedOperationException异常的方法,且不可变,这样避免误用,造成错误。

@Test
public void whenCreatesVavrList_thenCorrect() {List<Integer> intList = List.of(1, 2, 3);assertEquals(3, intList.length());assertEquals(new Integer(1), intList.get(0));assertEquals(new Integer(2), intList.get(1));assertEquals(new Integer(3), intList.get(2));
}

此外还可以通过提供的API执行计算任务。

@Test
public void whenSumsVavrList_thenCorrect() {int sum = List.of(1, 2, 3).sum().intValue();assertEquals(6, sum);
}

Vavr集合提供了在Java集合框架中绝大多数常见的类,并且实现了其所有特征。Vavr提供的集合工具使得编写的代码更加紧凑,健壮,并且提供了丰富的功能。

7. 验证Validation

Vavr将函数式编程中 Applicative Functor(函子)的概念引入Java。vavr.control.Validation类能够将错误整合。通常情况下,程序遇到错误就,并且未做处理就会终止。然而,Validation会继续处理,并将程序错误累积,最终最为一个整体处理。
例如我们希望注册用户,用户具有用户名和密码。我们会接收一个输入,然后

决定是否创建Person实例或返回一个错误。Person类如下。

public class Person {private String name;private int age;// setters and getters, toString
}

接着,创建一个PersonValidator类。每个变量都会有一个方法来验证。此外还有方法可以将所有的验证结果整合到一个Validation实例中。

class PersonValidator {String NAME_ERR = "Invalid characters in name: ";String AGE_ERR = "Age must be at least 0";public Validation<List<String>, Person> validatePerson(String name, int age) {return Validation.combine(validateName(name), validateAge(age)).ap(Person::new);}private Validation<String, String> validateName(String name) {String invalidChars = name.replaceAll("[a-zA-Z ]", "");return invalidChars.isEmpty() ?Validation.valid(name): Validation.invalid(NAME_ERR + invalidChars);}private Validation<String, Integer> validateAge(int age) {return age < 0 ? Validation.invalid(AGE_ERR): Validation.valid(age);}
}

验证规则为age必须大于0,name不能包含特殊字符。

@Test
public void whenValidationWorks_thenCorrect() {PersonValidator personValidator = new PersonValidator();Validation<List<String>, Person> valid =personValidator.validatePerson("John Doe", 30);Validation<List<String>, Person> invalid =personValidator.validatePerson("John? Doe!4", -1);assertEquals("Valid(Person [name=John Doe, age=30])",valid.toString());assertEquals("Invalid(List(Invalid characters in name: ?!4,Age must be at least 0))",invalid.toString());
}

Validation.Valid实例包含了有效值。Validation.Invalid包含了错误。因此validation要么
返回有效值要么返回无效值。Validation.Valid内部是一个Person实例,而Validation.Invalid是一组错误信息。

8. 延迟计算Lazy

Lazy是一个容器,表示一个延迟计算的值。计算被推迟,直到需要时才计算。此外,计算的值被缓存或存储起来,当需要时被返回,而不需要重复计算。

@Test
public void givenFunction_whenEvaluatesWithLazy_thenCorrect() {Lazy<Double> lazy = Lazy.of(Math::random);assertFalse(lazy.isEvaluated());double val1 = lazy.get();assertTrue(lazy.isEvaluated());double val2 = lazy.get();assertEquals(val1, val2, 0.1);
}

上面的例子中,我们执行的计算是Math.random。当我们调用isEvaluated检查状态时,发现函数并没有被执行。随后调用get方法,我们得到计算的结果。第2次调用get时,再次返回之前计算的结果,而之前的计算结果已被缓存。

9. 模式匹配Pattern Matching

当我们执行一个计算或根据输入返回一个满足条件的值时,我们通常会用到if语句。

@Test
public void whenIfWorksAsMatcher_thenCorrect() {int input = 3;String output;if (input == 0) {output = "zero";}if (input == 1) {output = "one";}if (input == 2) {output = "two";}if (input == 3) {output = "three";}else {output = "unknown";}assertEquals("three", output);
}

上述代码仅仅执行若干比较与赋值操作,没个操作都需要3行代码,当条件数量大增时,代码将急剧膨胀。当改为switch时,情况似乎也没有好转。

在Vavr中,我们通过Match方法替换switch块。每个条件检查都通过Case方法调用来替换。 $()来替换条件并完成表达式计算得到结果。

@Test
public void whenMatchworks_thenCorrect() {int input = 2;String output = Match(input).of(Case($(1), "one"),Case($(2), "two"),Case($(3), "three"),Case($(), "?"));assertEquals("two", output);
}

这样,代码变得紧凑,平均每个检查仅用一行。此外我们还可以通过谓词(predicate)来替换表达式。

Match(arg).of(Case(isIn("-h", "--help"), o -> run(this::displayHelp)),Case(isIn("-v", "--version"), o -> run(this::displayVersion)),Case($(), o -> run(() -> {throw new IllegalArgumentException(arg);}))
);

10. 总结

本文介绍了Vavr的基本能力,Vavr是基于Java 8的一个流行的函数式编程库。通过Vavr提供的功能我们可以改进我们的代码。

11. 原文地址

http://www.baeldung.com/vavr

Java8+函数式开发库Vavr使用入门相关推荐

  1. Java 函数式库Vavr功能

    1 概述 Vavr 是Java 8+中一个函数式库,提供了一些不可变数据类型及函数式控制结构. 1.1 Maven 依赖 添加依赖,可以到maven仓库中查看最新版本. <dependency& ...

  2. Java8函数式编程语法入门

    Java8函数式编程语法入门 Java8中函数式编程语法能够精简代码. 使用Consumer作为示例,它是一个函数式接口,包含一个抽象方法accept,这个方法只有输入而无输出. 现在我们要定义一个C ...

  3. WinCap网络开发库入门(转)

    Winpcap是一个强大的网络开发库,可以实现许多功能:获取可用的网络适配器:获取指定适配器信息(比如名称和描述信息):捕获指定网卡的数据封包:发送数据封包:过滤捕获的包以获取特定包等. 文章源地址: ...

  4. java8 函数式编程_使用Javaslang进行Java 8中的函数式编程

    java8 函数式编程 我们非常高兴地在jOOQ博客上宣布一个客座帖子,该帖子由HSH Nordbank的高级软件工程师Daniel Dietrich (三人的丈夫和父亲)撰写. 他目前作为项目负责人 ...

  5. java8 函数式编程_您必须学习Java 8的函数式编程吗?

    java8 函数式编程 我最近一直在研究Java 8,并掌握了Manning出版的" Java 8 In Action" . 让我印象深刻的第一件事是Java 8独特的销售主张是函 ...

  6. 可爱的python测试开发库(python测试开发工具库汇总)

    2019独角兽企业重金招聘Python工程师标准>>> 欢迎转载,转载请注明来源: github地址 谢谢点赞 本文地址 Python测试开发库 参考资料 https://githu ...

  7. C/C++ 类库开发库参考【资料整理】

    转自:http://bbs.chinaunix.net/thread-1858444-1-1.html 这里收集一些著名的 C/C++ 开发库.SDK.类库.可复用类与结构代码 等信息,列举它们的介绍 ...

  8. java8 函数式编程_Java 8函数式编程:延迟实例化

    java8 函数式编程 单例通常会延迟实例化自己,有时,如果对象足够重,则可以延迟实例化类字段. 通常,在走惰性路线时,getter方法(或accessor )必须具有一段代码,该代码块在返回对象之前 ...

  9. springmvc+activiti 完美整合- 流程在线设计+代码生成器+UI快速开发库,提高一半的开发效率

    JEECG(J2EE Code Generation) 是一款基于代码生成器的智能开发平台,采用代码生成+手工MERGE半智能开发模式, 可以帮助解决Java项目60%的重复工作,让开发更多关注业务逻 ...

  10. python游戏开发库_Python库之游戏开发及虚拟现实

    1.PyGame:简单的游戏开发功能库 http://www.pygame.org 提供了基于SDL的简单游戏开发功能及实现引擎 理解游戏对外部输入的响应机制及角色构建和交互机制 Python游戏入门 ...

最新文章

  1. Mysql中文乱码问题完美解决方案
  2. android设置app全局没通知声,从android中的firebase发送通知时没有通知声音
  3. python画平行坐标图_Matplotlib中的平行坐标图
  4. NHibernate 与Oracle 点滴
  5. python中模块sys与os的一些常用方法
  6. Linux 命令行上执行多个命令(分隔符简介使用)
  7. NOIP2016 复赛普及组第 1 题 买铅笔 方法一
  8. ip申请 web应用_网络协议端口TCP/IP概览
  9. MATLAB2016b安装教程
  10. PS钢笔工具使用方法简介
  11. HDU2881 Jack's struggle (LIS)
  12. 佐切的第三天学习分享
  13. 03 graphx 从 SSSP 来看 pregel
  14. BUUCTF笔记之Web系列部分WriteUp(三)
  15. 车金融|我在M公司的那两年
  16. 《机器学习实战:基于Scikit-Learn、Keras和TensorFlow第2版》-学习笔记(8):降维
  17. 华为智慧搜索 v9.1.2.300
  18. DecimalFormat - 格式化数据
  19. 调度增益控制基础概念Gain Scheduling Basics学习笔记
  20. 谷歌浏览器怎么将迅雷设置为默认下载方式 谷歌浏览器设置迅雷为默认下载的教程

热门文章

  1. 15. Magento路由分发过程解析(四):请求重写
  2. 第5讲 zend原理深度剖析
  3. hpm1216nfh驱动程序_惠普M1216nfh驱动下载
  4. 在windows2003, mysql5.0, PHP 4.4.4下的bugfree1.1打包
  5. tp5 自定义配置文件
  6. Django框架详细介绍---认证系统
  7. CSS3D写3d画廊滚动
  8. Python中文转拼音代码(支持全拼和首字母缩写)
  9. javascript中 try catch finally 的使用
  10. 进程间通信学习APUE学习---进程间通信(4)