大数据 java 代码示例_功能Java示例 第7部分–将失败也视为数据
大數(shù)據(jù) java 代碼示例
這是稱(chēng)為“ Functional Java by Example”的系列文章的第7部分。
我在本系列的每個(gè)部分中開(kāi)發(fā)的示例是某種“提要處理程序”,用于處理文檔。 之前我們已經(jīng)處理過(guò)特殊情況,但是我們將在功能上將它們作為數(shù)據(jù)來(lái)處理,更多。
如果您是第一次來(lái),最好是從頭開(kāi)始閱讀。 它有助于了解我們從何處開(kāi)始以及如何在整個(gè)系列中繼續(xù)前進(jìn)。
這些都是這些部分:
- 第1部分–從命令式到聲明式
- 第2部分–講故事
- 第3部分–不要使用異常來(lái)控制流程
- 第4部分–首選不變性
- 第5部分–將I / O移到外部
- 第6部分–用作參數(shù)
- 第7部分–將失敗也視為數(shù)據(jù)
- 第8部分–更多純函數(shù)
我將在每篇文章發(fā)表時(shí)更新鏈接。 如果您通過(guò)內(nèi)容聯(lián)合組織來(lái)閱讀本文,請(qǐng)查看我博客上的原始文章。
每次代碼也被推送到這個(gè)GitHub項(xiàng)目 。
優(yōu)雅失敗:回顧不多
這是我們以前留下的東西:
class FeedHandler {List handle(List changes,Function creator) {changes.findAll { doc -> isImportant(doc) }.collect { doc ->creator.apply(doc).thenApply { resource ->setToProcessed(doc, resource)}.exceptionally { e ->setToFailed(doc, e)}.get()}}private static boolean isImportant(doc) {doc.type == 'important'}private static Doc setToProcessed(doc, resource) {doc.copyWith(status: 'processed',apiId: resource.id)}private static Doc setToFailed(doc, e) {doc.copyWith(status: 'failed',error: e.message)}}提要處理程序的主要職責(zé)是“處理”已更改文檔的列表,這似乎是每次在文檔中創(chuàng)建“資源”并進(jìn)一步處理時(shí)。
在上一部分中,該函數(shù)已抽象為一個(gè)函數(shù),該函數(shù)接受Doc并返回Resource ,在Java中如下所示: Function creator
您可以看到資源實(shí)際上包裝在CompletableFuture (CF)中,這使我們可以鏈接方法調(diào)用,例如thenApply和exceptionally 。 在第3部分(不要使用異常來(lái)控制流程) ,我們推出了exceptionally更換,我們使用了部分try-catch來(lái)應(yīng)對(duì)可能的例外創(chuàng)建資源時(shí)。
當(dāng)時(shí)的代碼如下:
try {def resource = createResource(doc)updateToProcessed(doc, resource) } catch (e) {updateToFailed(doc, e) }我們將其替換為:
createResource(doc) .thenAccept { resource ->updateToProcessed(doc, resource) }.exceptionally { e ->updateToFailed(doc, e) }CF使我們能夠發(fā)出“異常”完成的信號(hào),而無(wú)需使用諸如拋出Exception類(lèi)的副作用。 在Java SDK中,這是封裝結(jié)果(成功或失敗)并與(例如) Optional (當(dāng)前值或空值)共享monadic屬性的少數(shù)類(lèi)之一。
在其他語(yǔ)言(例如Scala)中,有一種專(zhuān)用類(lèi)型稱(chēng)為T(mén)ry 。
嘗試
從Scala嘗試文檔:
Try類(lèi)型表示可能導(dǎo)致異常或返回成功計(jì)算值的計(jì)算。
使用Try Scala開(kāi)發(fā)人員無(wú)需在可能發(fā)生異常的任何地方進(jìn)行顯式異常處理。 如果我們也要在Java中使用它呢?
幸運(yùn)的是,有一個(gè)名為Vavr的庫(kù),其中包含我們可以在Java項(xiàng)目中使用的大量功能實(shí)用程序。
Vavr Try文檔中的示例向我們展示了完全忘記異常是多么容易:
Try.of(() -> bunchOfWork()).getOrElse(other);我們要么在成功時(shí)從bunchOfWork()獲得結(jié)果, bunchOfWork()在成功過(guò)程中因other而失敗。
此類(lèi)實(shí)際上是一個(gè)接口,并且有一堆默認(rèn)方法,它們都返回實(shí)例本身,從而可以無(wú)限地鏈接廣告 ,例如:
- andFinally –提供最終嘗試行為,無(wú)論操作結(jié)果如何。
- andThen –如果成功,則運(yùn)行給定的runnable,否則返回此Failure。
- filter –如果是失敗或成功,并且值滿足謂詞,則返回此值。
- onFailure –如果這是一次失敗,則消耗該throwable。
- onSuccess –如果成功,則使用該值。
- map –如果給定的函數(shù)成功,則運(yùn)行給定的檢查函數(shù),并將當(dāng)前表達(dá)式的結(jié)果傳遞給它。
返回最終值的方法:
- get –如果成功則獲取此Try的結(jié)果,如果失敗則獲取throw。
- getCause –如果失敗則獲取原因,如果成功則拋出。
- getOrElse –返回基礎(chǔ)值(如果存在),否則返回另一個(gè)值。
- getOrElseGet –返回基礎(chǔ)值(如果存在),否則返回另一個(gè)函數(shù)的值。
- getOrElseThrow –返回基礎(chǔ)值(如果存在),否則拋出getOrElseThrow ()。
- getOrElseTry –返回基礎(chǔ)值(如果存在),否則返回Try.of(supplier).get()的結(jié)果。
- getOrNull –返回基礎(chǔ)值(如果存在),否則返回null 。
將庫(kù)包含在項(xiàng)目中后,我們的代碼如何受益?
只需將我們的CompletableFuture替換為T(mén)ry 。
因此,將我們的調(diào)用替換為thenApply/exceptionally到map/getOrElseGet '
creator.apply(doc) .thenApply { resource ->// ... }.exceptionally { e ->// ... }.get()變成
creator.apply(doc) .map { resource ->// ... }.getOrElseGet { e ->// ... }Try的map -method接受一個(gè)函數(shù),該函數(shù)在try為“成功”時(shí)運(yùn)行(如前所述)。 getOrElseGet方法可以在發(fā)生故障(例如異常)的情況下接受函數(shù)。
您可以像在Stream那樣窺視內(nèi)部,例如
creator.apply(doc) .peek { resource ->println "We've got a $resource" } .map { resource ->// ... }.getOrElseGet { e ->// ... }或者,您可以添加更多日志記錄以進(jìn)行開(kāi)發(fā)或故障排除,例如
creator.apply(doc) .peek { resource ->println "We've got a $resource" }.onSuccess { resource ->println "Successfully created $resource" }.onFailure { e ->println "Bugger! Got a $e" }.map { resource ->// ... }.onSuccess { document ->println "Successfully processed $document" }.onFailure { e ->println "Bugger! Processing failed with $e" }.getOrElseGet { e ->// ... }從表面上看,似乎沒(méi)有太大變化。 它只是將一組方法調(diào)用替換為其他方法調(diào)用,在這種情況下,也就足夠了。
但是,您可以選擇“ Try CompletableFuture因?yàn)樗坪醺匀坏剡m合我們要實(shí)現(xiàn)的目標(biāo)-計(jì)算沒(méi)有“未來(lái)主義”,也沒(méi)有計(jì)劃或“在某個(gè)時(shí)間點(diǎn)”可用。
但是還有更多。
從故障中恢復(fù)
現(xiàn)在我們得到的是,如果資源創(chuàng)建者API失敗,則將任何失敗很好地包裝在Try ,因此我們可以輕松地遵循成功或失敗的路徑。
但是,如果某些故障對(duì)我們有意義 ,并且在某些情況下我們希望以其他方式失敗的情況仍然成功,該怎么辦?
好吧,我們可以從失敗中恢復(fù)過(guò)來(lái) ,并按照自己的意愿修改代碼。 我們可以使用下面的Try方法,并使用漂亮的方法簽名,稱(chēng)為recover(Class exception, Function f) 。
它的Javadoc讀為:
如果是成功或失敗,并且原因不能從cause.getClass()分配,則返回此值。 否則,嘗試使用f恢復(fù)失敗的異常,即調(diào)用Try.of(()-> f.apply((X)getCause())。
換句話說(shuō):對(duì)于特定類(lèi)型的異常,我們可以提供將失敗重新變?yōu)槌晒Φ墓δ堋?
首先,再次刪除多余的日志記錄和onSuccess/onFailure 。 現(xiàn)在,我們有一個(gè)Try ,一個(gè)成功場(chǎng)景的map和一個(gè)getOrElseGet ,錯(cuò)誤場(chǎng)景的map :
class FeedHandler {List handle(List changes,Function creator) {changes.findAll { doc -> isImportant(doc) }.collect { doc ->creator.apply(doc).map { resource ->setToProcessed(doc, resource)}.getOrElseGet { e ->setToFailed(doc, e)}}}// ...}如果“資源創(chuàng)建” API(即creator#apply調(diào)用)拋出例如DuplicateResourceException信號(hào),該信號(hào)表明我們正在創(chuàng)建的資源是重復(fù)項(xiàng) ,那么它已經(jīng)存在 ,該怎么辦?
我們可以使用recover功能!
List handle(List changes,Function creator) {changes.findAll { doc -> isImportant(doc) }.collect { doc ->creator.apply(doc).recover { t ->handleDuplicate(doc)}.map { resource ->setToProcessed(doc, resource)}.getOrElseGet { e ->setToFailed(doc, e)}}}private Resource handleDuplicate(Doc alreadyProcessed) {// find earlier saved, existing resource and return that onereturn repository.findById(alreadyProcessed.getApiId())}我們可以在自己的端查找一個(gè)重復(fù)項(xiàng)(因?yàn)樗呀?jīng)被處理過(guò)一次),所以我們的“ handleDuplicate”方法將返回任何令人滿意的流程 (即Resource ),并且繼續(xù)進(jìn)行處理,就好像什么都沒(méi)有發(fā)生一樣。
當(dāng)然,這只是一個(gè)示例,但是recover接受任何接受Throwable并返回Try函數(shù)。
多種故障:模式匹配
- 如果我們實(shí)際上需要確保僅在出現(xiàn)DuplicateResourceException情況下才處理“重復(fù)”情況,而不是像現(xiàn)在這樣,僅處理任何異常,該怎么辦?
- 如果API可能引發(fā)我們還需要專(zhuān)門(mén)處理的另一種類(lèi)型的異常該怎么辦? 我們?nèi)绾卧谔幚矶鄠€(gè)異常類(lèi)型的“選擇”之間進(jìn)行選擇?
這就是使用Vavr的Match API進(jìn)行模式匹配的地方。 我們可以創(chuàng)建一個(gè)Match的異常對(duì)象x (通過(guò)給使用recover ),同時(shí)給予靜態(tài)of -方法幾種情況選擇。
recover { x -> Match(x).of(Case($(instanceOf(DuplicateResourceException.class)), t -> handleDuplicate(doc)),Case($(instanceOf(SpecialException.class)), t -> handleSpecial(t)) )}這個(gè)$實(shí)際上是Vavr的靜態(tài)方法,有幾種重載的版本返回一個(gè)模式 。
這里的版本是接受Predicate的所謂“保護(hù)模式”。 請(qǐng)從Vavr Javadocs(使用純Java)中查看另一個(gè)示例:
String evenOrOdd(int num) {return Match(num).of(Case($(i -> i % 2 == 0), "even"),Case($(this::isOdd), "odd")); }boolean isOdd(int i) {return i % 2 == 1; }函數(shù)的組合( Case , $和Match )在Java中似乎有點(diǎn)奇怪,但是目前還沒(méi)有本機(jī)支持。 同時(shí),您可以將Vavr用于此類(lèi)功能。
在Java 12中,已經(jīng)有兩個(gè)預(yù)覽功能正在努力使所有這些變?yōu)楝F(xiàn)實(shí)。 是JEP 305:instanceof的模式匹配和JEP 325:開(kāi)關(guān)表達(dá)式
在本期文章中,我們已經(jīng)看到我們可以將故障用作數(shù)據(jù),例如,走一條替代路徑并返回到功能流程。
作為參考,代碼現(xiàn)在看起來(lái)如下:
class FeedHandler {List<Doc> handle(List<Doc> changes,Function<Doc, Try<Resource>> creator) {changes.findAll { doc -> isImportant(doc) }.collect { doc ->creator.apply(doc).recover { x -> Match(x).of(Case($(instanceOf(DuplicateResourceException.class)), t -> handleDuplicate(doc)),Case($(instanceOf(SpecialException.class)), t -> handleSpecial(t)))}.map { resource ->setToProcessed(doc, resource)}.getOrElseGet { e ->setToFailed(doc, e)}}}private Resource handleDuplicate(Doc alreadyProcessed) {// find earlier saved, existing resource and return that onereturn repository.findById(alreadyProcessed.getApiId())}private Resource handleSpecial(SpecialException e) {// handle special situationreturn new Resource()}private static boolean isImportant(doc) {doc.type == 'important'}private static Doc setToProcessed(doc, resource) {doc.copyWith(status: 'processed',apiId: resource.id)}private static Doc setToFailed(doc, e) {doc.copyWith(status: 'failed',error: e.message)}}由于Groovy 2.x解析器無(wú)法正確理解lambda語(yǔ)法,因此上述示例在GitHub上的示例實(shí)際上無(wú)法正確解析為Groovy,但當(dāng)然您也可以找到等效的Java版本 。
繼續(xù),自己Try 。
下次,我們將以更多功能結(jié)束本系列!
如果您有任何意見(jiàn)或建議,我很想聽(tīng)聽(tīng)他們的意見(jiàn)!
翻譯自: https://www.javacodegeeks.com/2019/05/functional-java-by-example-treat-failures-data-too.html
大數(shù)據(jù) java 代碼示例
總結(jié)
以上是生活随笔為你收集整理的大数据 java 代码示例_功能Java示例 第7部分–将失败也视为数据的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 一般家用台式电脑怎么配置比较好?
- 下一篇: 程序中抛出空指针异常_从Java应用程序