Hystrix是Netflix開源的限流、熔斷降級組件,去年發現Hystrix已經再也不更新了,而在github主頁上將我引導到了另外一個替代項目——resilience4j,這個項目是基於Java 8開發的,而且只使用了vavr庫,也就是咱們今天要介紹的主角。
java
既然要談vavr,那麼先要談爲何要使用vavr,vavr是爲了加強Java的函數式編程體驗的,那麼這裏先介紹下Java中的函數式編程。git
Java 8引入了函數式編程範式,思路是:將函數做爲其餘函數的參數傳遞,其實在Java 8以前,Java也支持相似的功能,可是須要使用接口實現多態,或者使用匿名類實現。不論是接口仍是匿名類,都有不少模板代碼,所以Java 8引入了Lambda表達式,正式支持函數式編程。github
比方說,咱們要實現一個比較器來比較兩個對象的大小,在Java 8以前,只能使用下面的代碼:面試
Compartor<Apple> byWeight = new Comparator<Apple>() { public int compare(Apple a1, Apple a2) { return a1.getWeight().compareTo(a2.getWeight()); } }
上面的代碼使用Lambda表達式能夠寫成下面這樣(IDEA會提示你作代碼的簡化):編程
Comparator<Apple> byWeight = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
關於Lambda表達式,你須要掌握的知識點有:後端
java.util.function
包裏,包括Predicate<T>、Function<T,R>、Supplier<T>、Consumer<T>和BinaryOperator<T>等等;受限於 Java 標準庫的通用性要求和二進制文件大小,Java 標準庫對函數式編程的 API 支持相對比較有限。函數的聲明只提供了 Function 和 BiFunction 兩種,流上所支持的操做的數量也較少。基於這些緣由,你也許須要vavr
來幫助你更好得使用Java 8進行函數式開發。數組
vavr是在嘗試讓Java擁有跟Scala相似的語法。vavr提供了不可變的集合框架;更好的函數式編程特性;元組。
緩存
Vavr實現了一套新的Java集合框架來匹配函數式編程範式,vavr提供的集合都是不可變的。在Java中使用Stream,須要顯示得將集合轉成steam的步驟,而在vavr中則免去了這樣的步驟。app
使用Java 8的代碼:框架
Arrays.asList(1, 2, 3).stream().reduce((i, j) -> i + j); IntStream.of(1, 2, 3).sum();
使用vavr實現相同的功能,則更加直接:
//io.vavr.collection.List List.of(1, 2, 3).sum();
Java 8提供了接受一個參數的函數式接口Function和接受兩個參數的函數式接口BiFunction,vavr則提供了最多能夠接受8個參數的函數式接口:Function0、Function一、Function二、Function三、Function4……Function8。
vavr還提供了更多函數式編程的特性:
在數學上,函數組合能夠用兩個函數造成第三個函數,例如函數f:X->Y和函數g:Y->Z能夠組合成h:g(f(x)),表示X->Z。這裏看個組合的例子:
public class VavrFunctionExample { @Test public void testCompose() { //使用andThen Function1<Integer, Integer> plusOne = a -> a + 1; Function1<Integer, Integer> multiplyByTwo = a -> a * 2; Function1<Integer, Integer> add1AndMultiplyBy2 = plusOne.andThen(multiplyByTwo); Assert.assertEquals(6, add1AndMultiplyBy2.apply(2).intValue()); //使用compose Function1<Integer, Integer> add1AndMultiplyBy2WithCompose = multiplyByTwo.compose(plusOne); Assert.assertEquals(6, add1AndMultiplyBy2WithCompose.apply(2).intValue()); } }
你是否是經常寫這種代碼:調用一個函數,判斷它的返回值是否符合需求,或者須要catch全部異常以防異常狀況,甚至是catch(Throwable t)。Lifting特性就是爲了解決這個問題而存在的,能夠在內部處理異常狀況,並將異常轉換成一個特殊的結果None,這樣函數外部就能夠用統一的模式去處理函數結果。舉個例子:
public class VavrFunctionExample { @Test public void testLifting() { Function2<Integer, Integer, Integer> divide = (a, b) -> a / b; Function2<Integer, Integer, Option<Integer>> safeDivide = Function2.lift(divide); // = None Option<Integer> i1 = safeDivide.apply(1, 0); Assert.assertEquals("None", i1.toString()); // = Some(2) Option<Integer> i2 = safeDivide.apply(4, 2); Assert.assertEquals(2, i2.get().intValue()); } }
柯里化(Currying)指的是將原來接受多個參數的函數變成新的接受一個參數的函數的過程。對於Java來講,能夠方便得提供默認值方法,這裏看個例子:
public class VavrFunctionExample { @Test public void testCurried() { Function2<Integer, Integer, Integer> sum = (a, b) -> a + b; Function1<Integer, Integer> add2 = sum.curried().apply(2); Assert.assertEquals(6, add2.apply(4).intValue()); } }
這是一種緩存,某個方法只須要執行一次,後面都會返回第一次的結果;可是在實際應用中用到的地方應該很少。
public class VavrFunctionExample { @Test public void testMemorize() { Function0<Double> hashCache = Function0.of(Math::random).memoized(); double randomValue1 = hashCache.apply(); double randomValue2 = hashCache.apply(); Assert.assertTrue(randomValue1 == randomValue1); } }
模式匹配是函數式編程語言中的概念,目前Java中還不支持這個特性,使用vavr能夠用Java寫模式匹配的代碼。Java中的switch...case語句只能針對常量起做用,而使用模式匹配則能夠對另外一個函數的返回結果起做用,功能很是搶到。下面的例子分別給出了使用if、switch...case、模式匹配三個語法實現一樣功能的例子,能夠看出,模式匹配有助於減小代碼行數。
import org.junit.Test; import static io.vavr.API.$; import static io.vavr.API.Case; import static io.vavr.API.Match; import static org.junit.Assert.assertEquals; public class VavrPatternExample { @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); } @Test public void whenSwitchWorksAsMatcher_thenCorrect() { int input = 2; String output; switch (input) { case 0: output = "zero"; break; case 1: output = "one"; break; case 2: output = "two"; break; case 3: output = "three"; break; default: output = "unknown"; break; } assertEquals("two", output); } @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); } }
本號專一於後端技術、JVM問題排查和優化、Java面試題、我的成長和自我管理等主題,爲讀者提供一線開發者的工做和成長經驗,期待你能在這裏有所收穫。