大数据 java 代码示例

这是称为“ Functional Java by Example”的系列文章的第7部分。

我在本系列的每个部分中开发的示例是某种“提要处理程序”,用于处理文档。 之前我们已经处理过特殊情况,但是我们将在功能上将它们作为数据来处理,更多。

如果您是第一次来,最好是从头开始阅读。 它有助于了解我们从何处开始以及如何在整个系列中继续前进。

这些都是这些部分:

  • 第1部分–从命令式到声明式
  • 第2部分–讲故事
  • 第3部分–不要使用异常来控制流程
  • 第4部分–首选不变性
  • 第5部分–将I / O移到外部
  • 第6部分–用作参数
  • 第7部分–将失败也视为数据
  • 第8部分–更多纯函数

我将在每篇文章发表时更新链接。 如果您通过内容联合组织来阅读本文,请查看我博客上的原始文章。

每次代码也被推送到这个GitHub项目 。

优雅失败:回顾不多

这是我们以前留下的东西:

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)}}

提要处理程序的主要职责是“处理”已更改文档的列表,这似乎是每次在文档中创建“资源”并进一步处理时。

在上一部分中,该函数已抽象为一个函数,该函数接受Doc并返回Resource ,在Java中如下所示: Function creator

您可以看到资源实际上包装在CompletableFuture (CF)中,这使我们可以链接方法调用,例如thenApplyexceptionally 。 在第3部分(不要使用异常来控制流程) ,我们推出了exceptionally更换,我们使用了部分try-catch来应对可能的例外创建资源时。

当时的代码如下:

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使我们能够发出“异常”完成的信号,而无需使用诸如抛出Exception类的副作用。 在Java SDK中,这是封装结果(成功或失败)并与(例如) Optional (当前值或空值)共享monadic属性的少数类之一。


在其他语言(例如Scala)中,有一种专用类型称为Try

尝试

从Scala尝试文档:

Try类型表示可能导致异常或返回成功计算值的计算。

使用Try Scala开发人员无需在可能发生异常的任何地方进行显式异常处理。 如果我们也要在Java中使用它呢?

幸运的是,有一个名为Vavr的库,其中包含我们可以在Java项目中使用的大量功能实用程序。

Vavr Try文档中的示例向我们展示了完全忘记异常是多么容易:

Try.of(() -> bunchOfWork()).getOrElse(other);

我们要么在成功时从bunchOfWork()获得结果, bunchOfWork()在成功过程中因other而失败。

此类实际上是一个接口,并且有一堆默认方法,它们都返回实例本身,从而可以无限地链接广告 ,例如:

  • andFinally –提供最终尝试行为,无论操作结果如何。
  • andThen –如果成功,则运行给定的runnable,否则返回此Failure。
  • filter –如果是失败或成功,并且值满足谓词,则返回此值。
  • onFailure –如果这是一次失败,则消耗该throwable。
  • onSuccess –如果成功,则使用该值。
  • map –如果给定的函数成功,则运行给定的检查函数,并将当前表达式的结果传递给它。

返回最终值的方法:

  • get –如果成功则获取此Try的结果,如果失败则获取throw。
  • getCause –如果失败则获取原因,如果成功则抛出。
  • getOrElse –返回基础值(如果存在),否则返回另一个值。
  • getOrElseGet –返回基础值(如果存在),否则返回另一个函数的值。
  • getOrElseThrow –返回基础值(如果存在),否则抛出getOrElseThrow ()。
  • getOrElseTry –返回基础值(如果存在),否则返回Try.of(supplier).get()的结果。
  • getOrNull –返回基础值(如果存在),否则返回null

将库包含在项目中后,我们的代码如何受益?

只需将我们的CompletableFuture替换为Try

因此,将我们的调用替换为thenApply/exceptionallymap/getOrElseGet '

creator.apply(doc)
.thenApply { resource ->// ...
}.exceptionally { e ->// ...
}.get()

变成

creator.apply(doc)
.map { resource ->// ...
}.getOrElseGet { e ->// ...
}

Try的map -method接受一个函数,该函数在try为“成功”时运行(如前所述)。 getOrElseGet方法可以在发生故障(例如异常)的情况下接受函数。

您可以像在Stream那样窥视内部,例如

creator.apply(doc)
.peek { resource ->println "We've got a $resource"
}
.map { resource ->// ...
}.getOrElseGet { e ->// ...
}

或者,您可以添加更多日志记录以进行开发或故障排除,例如

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 ->// ...
}

从表面上看,似乎没有太大变化。 它只是将一组方法调用替换为其他方法调用,在这种情况下,也就足够了。

但是,您可以选择“ Try CompletableFuture因为它似乎更自然地适合我们要实现的目标-计算没有“未来主义”,也没有计划或“在某个时间点”可用。

但是还有更多。

从故障中恢复

现在我们得到的是,如果资源创建者API失败,则将任何失败很好地包装在Try ,因此我们可以轻松地遵循成功或失败的路径。

但是,如果某些故障对我们有意义 ,并且在某些情况下我们希望以其他方式失败的情况仍然成功,该怎么办?

好吧,我们可以从失败中恢复过来 ,并按照自己的意愿修改代码。 我们可以使用下面的Try方法,并使用漂亮的方法签名,称为recover(Class exception, Function f)

它的Javadoc读为:

如果是成功或失败,并且原因不能从cause.getClass()分配,则返回此值。 否则,尝试使用f恢复失败的异常,即调用Try.of(()-> f.apply((X)getCause())。

换句话说:对于特定类型的异常,我们可以提供将失败重新变为成功的功能。

首先,再次删除多余的日志记录和onSuccess/onFailure 。 现在,我们有一个Try ,一个成功场景的map和一个getOrElseGet ,错误场景的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)}}}// ...}

如果“资源创建” API(即creator#apply调用)抛出例如DuplicateResourceException信号,该信号表明我们正在创建的资源是重复项 ,那么它已经存在 ,该怎么办?

我们可以使用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())}

我们可以在自己的端查找一个重复项(因为它已经被处理过一次),所以我们的“ handleDuplicate”方法将返回任何令人满意的流程 (即Resource ),并且继续进行处理,就好像什么都没有发生一样。


当然,这只是一个示例,但是recover接受任何接受Throwable并返回Try函数。

多种故障:模式匹配

  • 如果我们实际上需要确保在出现DuplicateResourceException情况下才处理“重复”情况,而不是像现在这样,仅处理任何异常,该怎么办?
  • 如果API可能引发我们还需要专门处理的另一种类型的异常该怎么办? 我们如何在处理多个异常类型的“选择”之间进行选择?

这就是使用Vavr的Match API进行模式匹配的地方。 我们可以创建一个Match的异常对象x (通过给使用recover ),同时给予静态of -方法几种情况选择。

recover { x -> Match(x).of(Case($(instanceOf(DuplicateResourceException.class)), t -> handleDuplicate(doc)),Case($(instanceOf(SpecialException.class)),  t -> handleSpecial(t))
)}

这个$实际上是Vavr的静态方法,有几种重载的版本返回一个模式

这里的版本是接受Predicate的所谓“保护模式”。 请从Vavr Javadocs(使用纯Java)中查看另一个示例:

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;
}

函数的组合( Case$Match )在Java中似乎有点奇怪,但是目前还没有本机支持。 同时,您可以将Vavr用于此类功能。

在Java 12中,已经有两个预览功能正在努力使所有这些变为现实。 是JEP 305:instanceof的模式匹配和JEP 325:开关表达式

在本期文章中,我们已经看到我们可以将故障用作数据,例如,走一条替代路径并返回到功能流程。

作为参考,代码现在看起来如下:

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解析器无法正确理解lambda语法,因此上述示例在GitHub上的示例实际上无法正确解析为Groovy,但当然您也可以找到等效的Java版本 。

继续,自己Try

下次,我们将以更多功能结束本系列!


如果您有任何意见或建议,我很想听听他们的意见!

翻译自: https://www.javacodegeeks.com/2019/05/functional-java-by-example-treat-failures-data-too.html

大数据 java 代码示例

大数据 java 代码示例_功能Java示例 第7部分–将失败也视为数据相关推荐

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

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

  2. java 示例_功能Java示例 第3部分–不要使用异常来控制流程

    java 示例 这是称为" Functional Java by Example"的系列文章的第3部分. 我在本系列的每个部分中开发的示例是某种"提要处理程序" ...

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

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

  4. java 示例_功能Java示例 第5部分–将I / O移到外部

    java 示例 这是称为" Functional Java by Example"的系列文章的第5部分. 在上一部分中,我们停止了对文档的变异,并返回了数据的副本. 现在,我们需要 ...

  5. java 示例_功能Java示例 第4部分–首选不变性

    java 示例 这是称为" Functional Java by Example"的系列文章的第4部分. 在上一部分中,我们讨论了一些副作用,并且我想进一步详细说明如何通过将不可变 ...

  6. java 示例_功能Java示例 第2部分–讲故事

    java 示例 这是称为" Functional Java by Example"的系列文章的第2部分. 我在本系列的每个部分中开发的示例是某种"提要处理程序" ...

  7. 挖雷 java代码思路_挖地雷问题(示例代码)

    问题描述 在一条公路上埋有若干堆地雷,每堆地雷有一定的数量,地雷堆的编号为1,2,-,N,例如,埋有地雷数量如下:8 14 2 17 33 26 15 17 19 6此时,地雷的数量可用一维数组A(N ...

  8. java代码识别_识别Java中的代码气味

    java代码识别 作为软件开发人员,我们不仅要编写有效的代码,而且还要编写可维护的代码,这是我们的责任. Martin Fowler在他的<重构:改进现有代码的设计>中将代码气味定义为: ...

  9. java 代码封装_封装 java代码

    Java工程师必知词汇:封装 |名词定义| 封装(Encapsulation)是将数据和处理数据的程序组合起来,仅对外公开接口,达到信息隐藏的功能.封装的优点是能减少耦合.Java定义对象都是在语法中 ...

最新文章

  1. 从词袋到Transfomer,NLP十年突破史
  2. java 中的 serialVersionUID
  3. C++ 画星号图形——空心矩形(核心代码记录)
  4. 12月5日 第二冲刺周期个人站立会议内容报告(第五天)
  5. 引入 JPEGCodec;JPEGImageEncoder; 图片处理(有的时候会报错)
  6. java 继承与多态 习题_JAVA基础 第4章继承与多态_练习题_200910
  7. 脏牛Linux本地提权漏洞复现(CVE-2016-5195、Linux、Android、提权)
  8. 一:ActiveMQ知识整理
  9. 使用Sigar做后台服务器管理时,遇到的linux上的问题
  10. 哲学家晚餐问题的Haskell求解
  11. android屏幕适配无效_AndroidAutoLayout
  12. 电路布线问题的动态规划实现(java)
  13. 破解安装IAR编辑器
  14. 【图像加密】基于Arnold置乱图像加密解密Matlab代码
  15. Windows文件所有者恢复TrustedInstaller权限
  16. python 温度 符号_python中如何将华氏温度转换为摄氏温度?
  17. 大写加下划线转换驼峰规则
  18. 快速获取SAS数据集观测数
  19. (VQA)LRTA: A Transparent Neural-Symbolic Reasoning Framework with Modular Supervision for Visual Que
  20. Elasticsearch 7.X-8.0 AggregationBuliders 相关聚合函数(一)计数指标-百分位数

热门文章

  1. C Looooops POJ - 2115
  2. [学习笔记] 二次剩余
  3. jzoj4673,CF578D-LCS again【统计,字符串,容斥】
  4. 【贪心】数据备份(P6320)
  5. [系统安全]使用OD编写连连看外挂
  6. Div1 小A抓小B tarjan双连通分量缩点+dfs
  7. Spark入门(二)多主standalone安装
  8. Spring Cloud 升级最新 Finchley 版本,踩了所有的坑
  9. Spring 注入内部 Beans
  10. JavaScript Function.arguments 属性详解