1 概述

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

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

<dependency><groupId>io.vavr</groupId><artifactId>vavr</artifactId><version>0.9.0</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

Java 函数式库Vavr功能相关推荐

  1. java 函数式编程 示例_功能Java示例 第1部分–从命令式到声明式

    java 函数式编程 示例 功能编程(FP)的目的是避免重新分配变量,避免可变的数据结构,避免状态并全程支持函数. 如果将功能性技术应用于日常Java代码,我们可以从FP中学到什么? 在这个名为&qu ...

  2. java 函数式编程 示例_功能Java示例 第8部分–更多纯函数

    java 函数式编程 示例 这是第8部分,该系列的最后一部分称为"示例功能Java". 我在本系列的每个部分中开发的示例是某种"提要处理程序",用于处理文档. ...

  3. 几种常见的java开源库,及其功能介绍

    1.Commons Math 是Apache上一个轻量级自容器的数学和统计计算方法包,包含大多数常用的数值算法. 2.LWJGL(Lightweight Java Game Library)可以帮助J ...

  4. java gradle构建_在Gradle中为JPMS构建Java 6-8库

    java gradle构建 通过提供Java 9 module-info.class了解如何使用Gradle构建支持JPMS( Java平台模块系统 )的Java 6-8库. 介绍 如果您需要JPMS ...

  5. java 函数式编程_Java函数式编程:Javaslang入门

    java 函数式编程 Java是一门古老的语言,并且该领域中有很多新手在他们自己的领域(JVM)上挑战Java. 但是Java 8到来并带来了一些有趣的功能. 这些有趣的功能使编写新的惊人框架(例如S ...

  6. java 基础包的功能_Java 8的功能基础

    java 基础包的功能 Java 8彻底改变了Java. 它很可能是过去10年中最重要的Java版本. 有很多新功能,包括默认方法,方法和构造函数引用以及lambda, 仅举几例 . 更有趣的功能之一 ...

  7. 在Gradle中为JPMS构建Java 6-8库

    通过提供Java 9 module-info.class来了解如何使用Gradle构建支持JPMS( Java平台模块系统 )的Java 6-8库. 介绍 如果您需要JPMS本身的介绍,请查看此概述 ...

  8. GitHub上那些值得一试的JAVA开源库--转

    原文地址:http://www.jianshu.com/p/ad40e6dd3789 作为一名程序员,你几乎每天都会使用到GitHub上的那些著名Java第三方库,比如Apache Commons,S ...

  9. java length()函数_小猿圈介绍java函数式编码结构及优势

    对于java大家都已经不陌生了吧,今天小猿圈Java讲师就分享一篇关于java函数式编码结构及优势的知识点,希望对于学习java的你有一定的帮助,想学习就需要积累. 探讨三种下一代JVM语言:Groo ...

最新文章

  1. 洛谷P2896 [USACO08FEB]一起吃饭Eating Together
  2. matlab fill 渐变,Matlab的渐变色填充(一)
  3. Python中的del用法
  4. boost::allocator_destroy的实例
  5. ASP.NET中使用JSON方便实现前台与后台的数据交换
  6. 步步为营 .NET 代码重构学习笔记 十
  7. 计算机应用基础第十一版答案,计算机应用基础 11.doc
  8. Python---面向对象(一)
  9. 汇编实现: C库常见函数,串操作指令作用
  10. 常用chrome插件常用FireFox插件
  11. qt 部署 错误_Qt 5.9 安装过程报错现象及解决方案
  12. EXCEL中输入的数字无法正常显示变成科学计数法
  13. 是指可以显示网页服务器或者文件,浏览器是指可以显示网页服务器或者文件系统的HTML文件(标准通用标记语言的一个应用)内容,并让用户与这些文件交互的一种软件。...
  14. 记一次namenode关机导致的问题
  15. 白帽SEO为什么更好?
  16. 小米手机第三方卡刷软件_小米手机通用详细图形刷机教程(四): 刷入第三方系统...
  17. 天兔(Lepus)监控系统快速安装部署
  18. 有一个棋盘,有64个方格,在第一个方格里面放1粒芝麻重量是0.00001kg,第二个里面放2粒,第三个里面放4,棋盘上放的所有芝麻的重量。
  19. 一、微信小程序-快速回顾(创建项目、项目结构分析)
  20. 自己动手利用CentOS6.5 搭建php环境安装discuz论坛

热门文章

  1. 如何使用字体图标做一个淘宝购物车案例
  2. 网吧网络建设实用方案精品集锦(转)
  3. rhino/grasshopper曲线偏移出现扭转
  4. VBPR(视觉贝叶斯个性化排名)论文总结
  5. react native在growth stack中的角色思考
  6. Redis-01-Nosql概述
  7. 利用无线物联网控制器实现无线激光测距
  8. 基于UMPC的移动电视演示
  9. List遍历remove的那些事
  10. AsyncTask原理