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

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

我在本系列的每个部分中发展的示例是某种“提要处理程序”,用于处理文档。 在前面的部分中,我从一些原始代码开始,并应用了一些重构来描述“什么”而不是“如何”。

为了帮助代码向前发展,我们需要摆脱良好的java.lang.Exception (免责声明:我们实际上无法摆脱它)这就是其中的内容。

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

这些都是这些部分:

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

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

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

快速了解异常

自Java 1.0以来,我们的java.lang.Exception一直存在-基本上在好时机和其他时候成为我们的敌人。

关于它们的讨论不多,但是如果您想阅读一些资源,这是我的最爱:

  • Java异常 (JavaWorld)
  • Java例外– GeeksforGeeks (geeksforgeeks.org)
  • 9种处理Java中异常的最佳实践 (stackify.com)
  • 异常处理的最佳实践 (onjava.com)
  • Java异常面试问答 (journaldev.com)
  • 带有示例的Java中的异常处理 (beginnersbook.com)
  • Java异常处理(Try-catch) (hackerrank.com)
  • 十大Java异常处理最佳实践– HowToDoInJava (howtodoinjava.com)
  • Java中的异常处理和断言– NTU (ntu.edu.sg)
  • 异常处理:最佳实践指南 (dzone.com)
  • 在Java中处理异常的9种最佳实践 (dzone.com)
  • 修复7个常见Java异常处理错误 (dzone.com)
  • Java惯例->已检查异常与未检查异常 (javapractices.com)
  • Java中异常的常见错误 MikaelStåldal的技术博客 (staldal.nu)
  • Java开发人员在使用异常时犯的11个错误 (medium.com/@rafacdelnero)
  • 检查异常是好是坏? (JavaWorld)
  • 检查异常:Java的最大错误 精简Java (literatejava.com)
  • 未经检查的异常-争议 (docs.oracle.com)
  • 已检查异常的麻烦 (artima.com)
  • Java例外:您(可能)做错了 (dzone.com)
  • Java理论与实践:异常辩论– IBM (ibm.com)
  • Java的检查异常是一个错误(这是我想做的 (radio-weblogs.com)
  • Buggy Java代码:Java开发人员犯的十大最常见错误 Toptal (toptal.com)

您已经在Java 8上了吗? 生活变得好多了! 我… 呃…哦,等等。

  • Java输入流的错误处理– Javamex (javamex.com)
  • 在Java流中处理检查的异常 (oreilly.com)
  • JDK 8流中的异常处理 (azul.com)
  • 带有异常的Java 8功能接口 (slieb.org)
  • 重新打包流中的异常– blog @ CodeFX (blog.codefx.org)
  • 如何在Java 8 Stream中处理异常? –堆栈溢出 (stackoverflow.com)
  • 检查异常和流| Benji的博客 (benjiweber.co.uk)
  • 一个关于检查异常和Java 8 Lambda表达式的 故事 (javadevguy.wordpress.com)– 很好的战争故事!
  • hgwood / java8-streams-and-exceptions (github.com)

好的,看来您不可能正确地做到这一点。

至少,阅读上述名单后,我们现在完全取决于对速度的话题��

幸运的是,我不必再写一篇博客文章,介绍上面的文章已经涵盖了95%的内容,但是在这里,我将重点讨论代码中实际存在的一个Exception ��

副作用

既然您正在阅读这篇文章,您可能会对为什么这一切都与函数式编程有关感兴趣。

在以更“实用的方式”处理代码的过程中,您可能会遇到“副作用”一词,这是一件“坏事”。

在现实世界中, 副作用是您不希望发生的事情 ,您可能会说它等同于“例外”情况(您会例外说明),但是在函数式编程上下文中它具有更严格的含义。

维基百科有关副作用的文章说:

副作用(计算机科学)在计算机科学中,如果函数或表达式在返回范围之外修改了某些状态或与其调用函数或外界有可观察的交互作用,则称该函数或表达式具有副作用。 …在函数式编程中,很少使用副作用。

因此,在本系列的前两篇文章之后,让我们看看我们的FeedHandler代码当前的样子:

class FeedHandler {Webservice webserviceDocumentDb documentDbvoid handle(List<Doc> changes) {changes.findAll { doc -> isImportant(doc) }.each { doc ->try {def resource = createResource(doc)updateToProcessed(doc, resource)} catch (e) {updateToFailed(doc, e)}}}private Resource createResource(doc) {webservice.create(doc)}private boolean isImportant(doc) {doc.type == 'important'}private void updateToProcessed(doc, resource) {doc.apiId = resource.iddoc.status = 'processed'documentDb.update(doc)}private void updateToFailed(doc, e) {doc.status = 'failed'doc.error = e.messagedocumentDb.update(doc)}}

在一个地方,我们尝试捕获异常,在那儿,我们循环浏览重要的文档,并尝试为其创建“资源”(无论是什么)。

try {def resource = createResource(doc)updateToProcessed(doc, resource)
} catch (e) {updateToFailed(doc, e)
}

在上面的代码中catch (e)catch (Exception e) Groovy缩写。

是的,这就是我们正在捕获的通用java.lang.Exception 。 可以是任何例外,包括NPE。

如果createResource方法没有引发异常,则将文档(“ doc”)更新为“已处理”,否则将其更新为“失败”。 顺便说一句,即使updateToProcessed也会引发异常,但是对于当前的讨论,我实际上只对成功创建资源感兴趣。

因此,上面的代码可以工作 (我已经通过单元测试来证明它:-)),但是我对try-catch语句不满意。 我只对成功创建资源感兴趣,而且很傻,我只能提出createResource要么返回成功的资源, 要么抛出异常。

抛出异常以表示出了点问题,躲开闪避,让调用者捕获该异常以进行处理,这是为什么发明了异常的原因呢? 而且比返回null更好吗?

它一直在发生。 采取一些我们喜欢的框架,例如JPA规范中的 EntityManager#find

啊! 返回null

返回值:
找到的实体实例;如果该实体不存在,则返回null

错误的例子。

函数式编程鼓励使用无副作用的方法(或:函数),以使代码更易于理解且更易于推理。 如果某个方法仅接受某些输入并每次都返回相同的输出(这使其成为一个函数),则各种优化都可以在后台进行,例如通过编译器或缓存,并行化等。

我们可以再次用函数(计算出的值)替换函数,这称为参考透明度 。

在上一篇文章中,我们已经将一些逻辑提取到了自己的方法中,例如下面的isImportant 。 给定相同的文档(具有相同的 type属性)作为输入,我们每次都会获得相同的 (布尔值)输出。

boolean isImportant(doc) {doc.type == 'important'
}

这里没有可观察到的副作用,没有全局变量被突变,没有日志文件被更新–它只是塞进,塞出

因此,我要说的是,通过我们的传统异常与外界交互的函数很少在函数式编程中使用。

我想做得更好更好

可选救援

正如石磊韦伯表示它:

关于如何在Java中有效使用异常有不同的观点。 有些人喜欢检查异常,有些人则认为这是一次失败的实验,他们更喜欢独占使用未检查异常。 其他人则完全避开异常,而赞成传递和返回诸如Optional或Maybe之类的类型。

好的,让我们尝试一下Java 8的Optional以便发出是否可以创建资源的信号。

让我们更改我们的webservice接口和createResource方法,以在Optional包装并返回我们的资源:

//private Resource createResource(doc) {
private Optional<Resource> createResource(doc) {webservice.create(doc)
}

让我们更改原始的try-catch

try {def resource = createResource(doc)updateToProcessed(doc, resource)
} catch (e) {updateToFailed(doc, e)
}

map (处理资源)和orElseGet (处理空的可选):

createResource(doc).map { resource ->updateToProcessed(doc, resource)}.orElseGet { /* e -> */updateToFailed(doc, e)}

很棒的createResource方法:返回正确结果,或者为空结果。

等一下! 唯一的例外e我们需要传递到updateToFailed走了 :我们有一个空的Optional替代。 我们不能存储的原因失败的原因 -这是我们做的必要性。

可能是Optional只是表示“缺席”,并且是我们此处目的不正确的工具。

出色的完成

如果没有try-catchmap-orElseGet ,我确实喜欢代码开始更多地反映操作“流程”的方式。 不幸的是,使用Optional更适合“得到一些东西”或“什么也没有得到”(这也建议使用maporElseGet类的名称),并且没有给我们提供记录失败原因的机会。

还有什么方法能够获得成功的结果或失败的原因,而仍然接近我们的阅读方式呢?

Future 。 更好的是: CompletableFuture

CompletableFuture (CF)知道如何返回值,这类似于Optional 。 通常,CF用于将来获取值 ,但这不是我们想要将其用于…的原因。

从Javadoc :

……支持……在完成时触发的行动的未来。

跳动,它可以表示“异常”完成 -给我机会对此采取行动。

让我们更改maporElseGet

createResource(doc).map { resource ->updateToProcessed(doc, resource)}.orElseGet { /* e -> */updateToFailed(doc, e)}

thenAccept (处理成功)并exceptionally (处理失败):

createResource(doc).thenAccept { resource ->updateToProcessed(doc, resource)}.exceptionally { e ->updateToFailed(doc, e)}

CompletableFuture#exceptionally方法接受一个带有我们实际失败原因的异常e的函数。

您可能会想: tomayto,tomahto。 首先,我们进行了try-catch ,现在我们进行了thenAccept-exceptionally ,那么有什么大不同?

好吧,我们显然不能摆脱特殊情况,但我们现在正在像Functionalville的居民那样思考:我们的方法开始成为函数 ,告诉我们有什么东西有事。

考虑到这是我们在第4部分中需要进行的少量重构,而在第5部分中,甚至更多地限制了代码中的副作用。

现在就这样

作为参考,这是重构代码的完整版本。

class FeedHandler {Webservice webserviceDocumentDb documentDbvoid handle(List<Doc> changes) {changes.findAll { doc -> isImportant(doc) }.each { doc ->createResource(doc).thenAccept { resource ->updateToProcessed(doc, resource)}.exceptionally { e ->updateToFailed(doc, e)}}}private CompletableFuture<Resource> createResource(doc) {webservice.create(doc)}private boolean isImportant(doc) {doc.type == 'important'}private void updateToProcessed(doc, resource) {doc.apiId = resource.iddoc.status = 'processed'documentDb.update(doc)}private void updateToFailed(doc, e) {doc.status = 'failed'doc.error = e.messagedocumentDb.update(doc)}}

-

翻译自: https://www.javacodegeeks.com/2018/01/functional-java-example-part-3-dont-use-exceptions-control-flow.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/347860.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安全模式打开计算机策略,安全模式下怎么解除组策略的锁定?

2006-01-13注册表被锁住了&#xff0c;组策略也被禁用&#xff0c;安全模式也进不了&#xff0c;该如何修改注册表随便从网络上下在一个注册表编辑器,展开[hkey-current-user\software\microsoft\windows\current version\policies\system]主键,将键名 disableregistrytools 的…

Java基本语法(12)--分支结构if-else

基本格式结构 if (条件语句){条件语句为true时&#xff0c;进入执行的语句&#xff1b; }else{条件语句为false时&#xff0c;执行的语句&#xff1b; }if (条件语句1){条件语句1为true时&#xff0c;进入执行的语句&#xff1b; }else if(条件语句2){条件语句1为false&#x…

服务器硬盘 主板,服务器主板和普通主板有什么区别?

什么是工控服务器?首先我们来看专业上服务器是怎样定义的&#xff1a;工控服务器是一种高性能计算机&#xff0c;作为网络的节点&#xff0c;存储、处理网络上80%的数据、信息&#xff0c;因此也被称为网络的灵魂。也可以这样讲&#xff0c;工控服务器是指一个管理资源并为用户…

键盘输入Scanner类方法属性使用

基本步骤 1.导包&#xff1a;import java.util.Scanner 2.Scanner实例化&#xff0c;创建Scanner对象&#xff1a; Scanner scan new Scanner(System.in) 3.调用Scanner类相关方法&#xff08;next&#xff08;&#xff09;/nextXxx()&#xff09;&#xff0c;来获取指定数据类…

传奇霸业维护服务器,37传奇霸业8月18日部分区服维护计划

亲爱的玩家:您好&#xff0c;为保证服务器稳定运营&#xff0c;优化游戏体验&#xff0c;《传奇霸业》新版本“炼狱魔域”将代替旧版本“超霸传奇”继续向各大玩家提供游戏服务&#xff0c; 我们将在2016年8月18日对以下区服进行停服维护更新&#xff0c;具体维护时间将视情况提…

2018年机器学习趋势与Apache Kafka生态系统相结合

在慕尼黑举行的OOP 2018大会上&#xff0c;我介绍了有关使用Apache Kafka生态系统和诸如TensorFlow&#xff0c;DeepLearning4J或H2O之类的深度学习框架构建可扩展&#xff0c;关键任务微服务的演讲的更新版本。 我想分享更新后的幻灯片&#xff0c;并讨论一些有关最新趋势的更…

Java基本语法(13)--条件分支switch-case结构

基本格式结构&#xff1a; switch (表达式/变量) {case 常量1&#xff1a;执行语句1; //情况1case 常量2&#xff1a;执行语句2; //情况2... ... ...default: 以上情况都不满足时执行语句; }说明&#xff1a; switch结构中的表达式/变量只能是如下6种数据类型之一&#xff…

小米岭南通服务器维护,小米岭南通交通联合卡内测开启

原标题&#xff1a;小米岭南通交通联合卡内测开启IT之家12月21日消息 小米岭南通交通联合卡内测开启招募报名&#xff0c;持小米8(含探索版、屏幕指纹版)、小米MIX 系列(不含MIX 3)、小米Note 3/2、小米6/5s/5s Plus手机的用户可开通尝鲜。支持系统小米8和MIX 2S最低支持8.12.2…

mysql 大于号 优化_SQL优化 MySQL版 - 避免索引失效原则(二)

避免索引失效原则(二)注&#xff1a;继上一篇文章继续讲解&#xff1a;避免索引失效原则(一)https://www.cnblogs.com/StanleyBlogs/p/10482048.html#4195062作者 &#xff1a; Stanley 罗昊【转载请注明出处和署名&#xff0c;谢谢&#xff01;】体验SQL优化中的概率情况在上一…

mysql 唯一索引 死锁_MySQL 死锁套路:唯一索引 S 锁与 X 锁的爱恨情仇

毫不夸张的说&#xff0c;有一半以上的死锁问题由唯一索引贡献&#xff0c;后面介绍的很多死锁的问题都跟唯一索引有关。这次我们讲一段唯一索引 S 锁与 X 锁的爱恨情仇我们来看一个简化过的例子# 构造数据CREATE TABLE t1 (id int(11) NOT NULL AUTO_INCREMENT,name varchar(1…

ejb 示例 2018_EJB钝化和激活示例

ejb 示例 2018在本教程中&#xff0c;我们将了解状态Java企业会话Bean中激活和钝化的工作方式。 1.简介 有状态会话Bean通常保存有关特定客户端的信息&#xff0c;并在整个会话中保存该信息。 但是&#xff0c;事实是&#xff0c;客户端会话往往会在相当长的时间内保持活动状态…

js微信小程序页面左上角返回跳转指定页面

微信小程序非导航栏tabBar页面左上角返回默认返回上一次的页面&#xff08;即进入当前页面的前一页面&#xff09;&#xff0c;如果需要自定义页面&#xff0c;可以通过js中onUnload函数进行指定页面跳转。 ①关闭所有页&#xff0c;打开url指定页面 onUnload: function () {…

服务器mysql显示链接次数太多,服务器mysql显示链接次数太多

服务器mysql显示链接次数太多 内容精选换一换在本章节中&#xff0c;您将会把游戏应用部署到CCE云容器引擎中&#xff0c;您需要执行以下操作&#xff1a;创建集群&#xff1a;集群是运行应用的逻辑分组&#xff0c;包含一组云服务器资源&#xff0c;每个节点对应一台云服务器。…

使用JUnit 5在Mockito中方便地进行模拟–官方方式

从版本2.17.0开始&#xff0c;如果使用JUnit 5&#xff0c; Mockito提供了官方&#xff08;内置&#xff09;支持来管理模拟生命周期。 入门 为了利用该集成&#xff0c;需要在JUnit 5的junit-platform-engine旁边添加Mockito的mockito-junit-jupiter依赖项&#xff08;有关详…

esp32搭建文件服务器,ESP32入门示例 - SD卡Web服务器

这个是来自ESP32官方示例的改版&#xff0c;官方的示例由于存在一些问题所以我进行了修改原本的示例有点逻辑上的问题&#xff0c;所以进行了一些修改主要修改有&#xff1a;1.新增SD卡测试部分 复制自官方SD卡示例2.新增一个根目录页&#xff0c;访问根目录就可以看到3.修改了…

mysql 配置文件在哪_MySQL+MyCat分库分表 读写分离配置

一、 MySQLMyCat分库分表1 MyCat简介java编写的数据库中间件Mycat运行环境需要JDK。Mycat是中间件&#xff0c;运行在代码应用和MySQL数据库之间的应用。前身&#xff1a; corba&#xff0c;是阿里开发的数据库中间件&#xff0c;实现MySQL数据库分库分表集群管理的中间件&…

字符串String截取字符char

字符串类的charAt() 方法可返回指定位置的字符。 stringObject.charAt(index)从键盘输入获取了一个字符串&#xff0c;将其数据类型转换为字符型。 Scanner scan new Scanner(System.in); String str scan.next(); char ca str.charAt(0);示例&#xff1a; import java.u…

Java基本语法(14)--for循环结构

循环结构&#xff1a;在某些条件满足的情况下&#xff0c;反复执行特定代码的功能。 基本格式&#xff1a; for (①初始化部分; ②循环条件部分; ④迭代部分)&#xff5b; ③循环体部分; &#xff5d;如果①&#xff0c;④部分多条语句&#xff0c;语句之间用“&#xff0c;”…

Java 8中的功能接口是什么? @功能注释和示例

函数接口是Java 8最重要的概念之一&#xff0c;实际上为lambda表达式提供了动力&#xff0c;但是许多开发人员没有首先了解函数接口在Java 8中的作用就花了很多精力来理解它&#xff0c;并花时间学习lambda表达式和Stream API。除非您知道什么是功能接口以及lambda与它之间的关…

win10存储池_3个光威480G SSD组WIN10存储池,深度测试到底值不值得搞

上次由于我SSD不够&#xff0c;所以我用虚拟硬盘的方式&#xff0c;虚拟了3个VHDX硬盘&#xff0c;组了个奇偶校验的存储池&#xff0c;并且简单的做了测试。测试结果是&#xff0c;组存储池确实提高了我们的数据安全性。WIN10存储池&#xff0c;可以让我们玩家省去组RAID&…