摘要:是在嘗試讓擁有跟類似的語法。在中使用,需要顯示得將集合轉成的步驟,而在中則免去了這樣的步驟。中的語句只能針對常量起作用,而使用模式匹配則可以對另一個函數的返回結果起作用,功能非常搶到。
Hystrix是Netflix開源的限流、熔斷降級組件,去年發現Hystrix已經不再更新了,而在github主頁上將我引導到了另一個替代項目——resilience4j,這個項目是基于Java 8開發的,并且只使用了vavr庫,也就是我們今天要介紹的主角。
既然要談vavr,那么先要談為什么要使用vavr,vavr是為了增強Java的函數式編程體驗的,那么這里先介紹下Java中的函數式編程。
Java 8引入了函數式編程范式,思路是:將函數作為其他函數的參數傳遞,其實在Java 8之前,Java也支持類似的功能,但是需要使用接口實現多態,或者使用匿名類實現。不管是接口還是匿名類,都有很多模板代碼,因此Java 8引入了Lambda表達式,正式支持函數式編程。
比方說,我們要實現一個比較器來比較兩個對象的大小,在Java 8之前,只能使用下面的代碼:
CompartorbyWeight = new Comparator () { public int compare(Apple a1, Apple a2) { return a1.getWeight().compareTo(a2.getWeight()); } }
上面的代碼使用Lambda表達式可以寫成下面這樣(IDEA會提示你做代碼的簡化):
ComparatorbyWeight = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
關于Lambda表達式,你需要掌握的知識點有:
Lambda表達式可以理解為是一種匿名函數:它沒有名稱,但是又參數列表、函數主體、返回類型,可能還有一個可以拋出的異常列表;
函數式接口就是僅僅聲明了一個抽象方法的接口;
@FunctionalInterface注解對于函數式接口的作用,類似于@Override對于被重寫的方法——不是必須的,但是用了有助于提升代碼的可讀性,因此如果你在開發中自己定義函數式接口,最好也使用這個注解修飾;
Java 8自帶一些常用的函數式接口,放在java.util.function包里,包括Predicate
受限于 Java 標準庫的通用性要求和二進制文件大小,Java 標準庫對函數式編程的 API 支持相對比較有限。函數的聲明只提供了 Function 和 BiFunction 兩種,流上所支持的操作的數量也較少。基于這些原因,你也許需要vavr
來幫助你更好得使用Java 8進行函數式開發。
vavr是在嘗試讓Java擁有跟Scala類似的語法。vavr提供了不可變的集合框架;更好的函數式編程特性;元組。
Vavr實現了一套新的Java集合框架來匹配函數式編程范式,vavr提供的集合都是不可變的。在Java中使用Stream,需要顯示得將集合轉成steam的步驟,而在vavr中則免去了這樣的步驟。
vavr的List是不可變的鏈表,在該鏈表對象上的操作都會生成一個新的鏈表對象。
使用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();
vavr的Stream是惰性鏈表,元素只有在必要的時候才會參與計算,因此大部分操作都可以在常量時間內完成。
函數(Functions)Java 8提供了接受一個參數的函數式接口Function和接受兩個參數的函數式接口BiFunction,vavr則提供了最多可以接受8個參數的函數式接口:Function0、Function1、Function2、Function3、Function4……Function8。
vavr還提供了更多函數式編程的特性:
組合(Composition)
在數學上,函數組合可以用兩個函數形成第三個函數,例如函數f:X->Y和函數g:Y->Z可以組合成h:g(f(x)),表示X->Z。這里看個組合的例子:
public class VavrFunctionExample { @Test public void testCompose() { //使用andThen Function1plusOne = a -> a + 1; Function1 multiplyByTwo = a -> a * 2; Function1 add1AndMultiplyBy2 = plusOne.andThen(multiplyByTwo); Assert.assertEquals(6, add1AndMultiplyBy2.apply(2).intValue()); //使用compose Function1 add1AndMultiplyBy2WithCompose = multiplyByTwo.compose(plusOne); Assert.assertEquals(6, add1AndMultiplyBy2WithCompose.apply(2).intValue()); } }
Lifting
你是不是常常寫這種代碼:調用一個函數,判斷它的返回值是否符合需求,或者需要catch所有異常以防異常情況,甚至是catch(Throwable t)。Lifting特性就是為了解決這個問題而存在的,可以在內部處理異常情況,并將異常轉換成一個特殊的結果None,這樣函數外部就可以用統一的模式去處理函數結果。舉個例子:
public class VavrFunctionExample { @Test public void testLifting() { Function2divide = (a, b) -> a / b; Function2 > safeDivide = Function2.lift(divide); // = None Option i1 = safeDivide.apply(1, 0); Assert.assertEquals("None", i1.toString()); // = Some(2) Option i2 = safeDivide.apply(4, 2); Assert.assertEquals(2, i2.get().intValue()); } }
柯里化方法(Curring)
柯里化(Currying)指的是將原來接受多個參數的函數變成新的接受一個參數的函數的過程。對于Java來說,可以方便得提供默認值方法,這里看個例子:
public class VavrFunctionExample { @Test public void testCurried() { Function2sum = (a, b) -> a + b; Function1 add2 = sum.curried().apply(2); Assert.assertEquals(6, add2.apply(4).intValue()); } }
記憶化方法(Memorization)
這是一種緩存,某個方法只需要執行一次,后面都會返回第一次的結果;但是在實際應用中用到的地方應該不多。
public class VavrFunctionExample { @Test public void testMemorize() { Function0模式匹配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); } }參考資料
《Java 8實戰》
https://github.com/resilience4j/resilience4j
https://www.baeldung.com/vavr
https://www.vavr.io/vavr-docs/
本號專注于后端技術、JVM問題排查和優化、Java面試題、個人成長和自我管理等主題,為讀者提供一線開發者的工作和成長經驗,期待你能在這里有所收獲。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/75053.html
摘要:面向對象常見的設計模式有策略模式模板方法觀察者模式責任鏈模式以及工廠模式,使用表達式函數式編程思維有助于避免面向對象開發中的那些固定代碼。 本文是一篇《Java 8實戰》的閱讀筆記,閱讀大約需要5分鐘。 有點標題黨,但是這確實是我最近使用Lambda表達式的感受。設計模式是過去的一些好的經驗和套路的總結,但是好的語言特性可以讓開發者不去考慮這些設計模式。面向對象常見的設計模式有策略模式...
摘要:問對于程序員修煉之道你有下一步的計劃嗎程序員修煉之道這個項目很好,寫作的過程也很愉快。而最上層的程序員則是時刻對技藝以及技術的本質著迷。這也是的一大優勢。 非商業轉載請注明作譯者、出處,并保留本文的原始鏈接:http://www.ituring.com.cn/article/127453 Ben Evans是jClarity的聯合創始人。其公司致力于開發可以為開發和運維團隊提...
摘要:通過如下命令發布控制臺,運行編寫的默認程序。默認禁用,啟用它需要打開并取消注釋以下行。啟用數據庫啟動應用程序的數據庫,框架提供了內置的數據庫的支持。當用戶發出請求到,一個新的將被創建。方法為給定的獲取,把這個轉換成格式并返回響應。 編者注:我們發現了有趣的系列文章《30天學習30種新技術》,正在翻譯,一天一篇更新,年終禮包。下面是第 30 天的內容。 今天是最后一天,我決定學習一...
摘要:本文介紹和點評上的等并發編程模型。異步更適合并發編程。同步使線程阻塞,導致等待。基本模型這是最簡單的模型,創建線程來執行一個任務,完畢后銷毀線程。響應式編程是一種面向數據流和變化傳播的編程模式。起源于電信領域的的編程模型。 本文介紹和點評JVM上的Thread, Thread Pool, Future, Rx, async-await, Fiber, Actor等并發編程模型。本人經驗...
摘要:本文介紹和點評上的等并發編程模型。異步更適合并發編程。同步使線程阻塞,導致等待。基本模型這是最簡單的模型,創建線程來執行一個任務,完畢后銷毀線程。響應式編程是一種面向數據流和變化傳播的編程模式。起源于電信領域的的編程模型。 本文介紹和點評JVM上的Thread, Thread Pool, Future, Rx, async-await, Fiber, Actor等并發編程模型。本人經驗...
閱讀 1207·2021-09-03 10:44
閱讀 603·2019-08-30 13:13
閱讀 2796·2019-08-30 13:11
閱讀 1967·2019-08-30 12:59
閱讀 1034·2019-08-29 15:32
閱讀 1595·2019-08-29 15:25
閱讀 987·2019-08-29 12:24
閱讀 1277·2019-08-27 10:58