重新打包流中的异常

Java 8已有两年的历史了,但是仍然存在社区尚未为其开发好的解决方案的用例,甚至边缘用例。 如何处理流管道中的已检查异常就是这样的问题之一。 Stream操作接受的功能接口不允许实现抛出已检查的异常,但是我们可能要调用许多方法。 显然,这里存在一种紧张关系,许多开发人员都曾遇到过这种紧张关系。

我想在简短的系列文章中探讨这个主题:

重新打包流中的异常
重新打包异常以便抛出它们,而编译器不会抱怨它。
处理流中的异常
可以通过延迟错误处理来现场捕获并处理异常。
流中抛出异常
毕竟如何通过引发异常来处理延迟的错误。

我的主要目标是提出各种解决方案,并且在理想情况下,建立使讨论更容易的通用术语。 我还将对我的建议发表评论,并添加自己的评估意见-尽管这是次要的,但我希望它不会偏离主要目标:将想法付诸实践。

第一篇文章将研究重新打包异常,以便编译器停止抱怨。

设置场景

基本场景是流的每个频繁用户遇到的一种或多种形式:您想在流的中间操作之一中使用的方法将引发检查的异常。

在本文中,我将假定您正在尝试将字符串流解析为用户流:

Stream<User> parse(Stream<String> strings) {return strings.map(User::parse);
}

(如果您不打算将流作为参数或返回值,请假设整个流管道都在方法的范围之内。以下两种方法都可以应用,但是如果您对流中的整个流进行处理,则某些评估会有所不同。点。)

不幸的是, User::parse可以抛出ParseException

public class User {public static User parse(String userString) throws ParseException {// ...}}

这导致编译器抱怨方法参考User::parse “未处理的异常:java.text.ParseException” 。 现在做什么?

在我们研究该问题的解决方案之前,我想指出一点:我不认为Stream API与检查异常的不兼容性是可以通过其他设计克服的。 在某个时候,我可能会写一个更长的帖子来解释这一点,但是简短的版本是这样的:如果功能接口方法可能引发检查异常,那么将没有一种将流与惰性结合在一起的令人愉悦的方法,因为它将是终端操作最终抛出该异常。

但是我们可以充分利用可以引发异常的函数,因此让我们在介绍该接口时对其进行介绍:

@FunctionalInterface
interface CheckedFunction<T, R, EX extends Exception> {R apply(T element) throws EX;}

这使我们可以将User::parse分配给CheckedFunction<StringUser, ParseException> 。 请注意,异常的类型是通用的,稍后将派上用场。

重新打包流中的异常

那么,您真的必须处理例外情况吗? 不知道,您能不能使问题解决? 令人惊讶的答案是“是的,您可以。” 是否应该拭目以待……

包装未检查的异常

给定一个引发检查异常的函数,将其转换为引发未检查异常的函数非常容易:

Stream<User> parse(Stream<String> strings) {return strings.map(uncheckException(User::parse))
}<T, R> Function<T, R> uncheckException(CheckedFunction<T, R, Exception> function) {return element -> {try {return function.apply(element);} catch (Exception ex) {// thanks to Christian Schneider for pointing out// that unchecked exceptions need not be wrapped againif (ex instanceof RuntimeException)throw (RuntimeException) ex;elsethrow new RuntimeException(ex);}};
}

这实际上还不错。 而且,无论如何,如果您更喜欢未检查的异常,那么这将更具吸引力。 另一方面,如果您重视检查的异常(对于您期望的事情可能会出错,例如错误的输入)与未检查的异常(对于实现错误)之间的区别,那么这将使您不寒而栗。

在任何情况下,流的最终使用者都必须意识到可能会引发异常,这时需要与测试或文档进行通信,这两者都比编译器更容易忽略。 感觉就像在小河里藏了一颗炸弹。

最后,请注意,这会在第一个错误发生时立即中止流-可能会或可能不会发生的事情。 如果该方法返回一个流而不是使用它,则很难确定是否可行,因为不同的调用者可能有不同的要求。

偷偷摸摸的异常

解决整个问题的另一种方法是“偷偷地”抛出异常。 该技术使用泛型来混淆编译器,并使用@SuppressWarnings使其剩余的投诉静音。

Stream<User> parse(Stream<String> strings) {return strings.map(hideException(User::parse));
}<T, R> Function<T, R> hideException(CheckedFunction<T, R, Exception> function) {return element -> {try {return function.apply(element);} catch (Exception ex) {return sneakyThrow(ex);}};
}@SuppressWarnings("unchecked")
<E extends Throwable, T> T sneakyThrow(Throwable t) throws E {throw (E) t;
}

嗯,什么? 如所承诺的, sneakyThrow方法使用泛型来欺骗编译器以sneakyThrow未经检查的异常而不声明它。 然后hideException使用它来捕获CheckedFunction可能抛出的任何异常,然后hideException将其重新抛出。 (如果您使用的是Lombok,请查看其@SneakyThrows批注 。)

我认为这是非常冒险的举动。 对于它,它仍然在小河中隐藏了一颗炸弹。 但是,它走得更远,这使得炸弹难以妥善化解。 您是否曾经尝试捕获未使用throws子句声明的检查异常?

try {userStrings.stream().map(hideException(User::parse));.forEach(System.out::println);
// compile error because ParseException
// is not declared as being thrown
} catch (ParseException ex) {// handle exception
}

无法工作,因为编译器在没有方法实际抛出ParseException的假设下运行。 相反,您必须捕获Exception ,过滤掉ParseException并重新抛出其他所有内容。

哇,真烂!

不幸的是,这种技术出现在StackOverflow答案中 ,当寻找Java流异常处理时,它在Google上的排名非常高。 公平地说,答案包含免责声明,但恐怕它可能会经常被忽略:

不用说,应该谨慎处理,项目中的每个人都必须意识到,未经声明的异常可能会出现在经过检查的异常中。

但是,正如我们已经看到的那样,没有很好的方法来声明/捕获这样的异常,所以我要说的是:

这是一个不错的实验,但从未真正做到! 如果确实要抛出,请包装运行时异常。

电梯例外

偷偷摸摸的问题是,它使流的消费者感到惊讶, 并且即使他们克服了这种惊讶,也很难处理该异常。 对于后者,至少有一个出路。 考虑以下功能:

<T, R, EX extends Exception> Function<T, R> liftException(CheckedFunction<T, R, EX> function) throws EX {return hideException(function);
}

它与hideException完全相同, 但是声明它抛出EX。 为什么会有帮助? 因为可以通过这种方式使编译器理解可能会抛出检查异常:

Stream<User> parse(Stream<String> strings) {return strings// does not compile because `liftException`// throws ParseException but it is unhandled.map(liftException(User::parse));
}

问题是, liftException的主体非常清楚地表明,它当然不会引发异常。 因此,在这样的示例中,我们只能看到部分管道,因此可以说使情况更加混乱。 现在,解析调用者可能会将其放入try-catch块中,期望能够很好地处理异常(如果不要对它太认真地考虑),然后当终端操作抛出该异常时仍会感到惊讶(记住它被sneakyThrow隐藏)。

但是,如果您是从不返回流的人, liftException非常有用。 有了它,您的流管道中的一些调用就声明抛出一个检查异常,因此您可以将其全部放入try-catch块中:

try {userStrings.stream().map(liftException(User::parse));.forEach(System.out::println);
} catch (ParseException ex) {// handle exception
}

或者,包含管道的方法可以声明抛出异常:

List<User> parse(List<String> userStrings) throws ParseException {return userStrings.stream().map(liftException(User::parse));.collect(toList());
}

但是正如我之前所说,我认为只有在您永不返回流的情况下,此方法才有效。 因为如果这样做(即使只是偶尔这样做),您或您的同事就有可能在重构期间将管道拆开,从而使炸弹处于未声明的检查异常的状态,从而将其隐藏在流中。

Sebastian Millies指出了另一个缺点,即到目前为止使用的接口和方法仅允许一个例外。 一旦一种方法声明了多个检查异常,事情就会成问题。 要么让Java派生一个公共的超类型(可能是Exception ), liftException为一个以上的异常声明其他CheckedFunction接口和liftException方法。 两者都不是很好的选择。

给定抛出异常的方法,如果需要立即抛出异常,我向您展示了两种不同的方式在流中使用它们:

  • 将检查的异常包装在运行时异常中
  • 偷偷地抛出已检查的异常,以便编译器无法识别被抛出的异常
  • 仍然偷偷摸摸地抛出,但是让utitility函数声明异常,以便编译器至少知道它被抛出了

请注意,所有这些方法都意味着流管线将在那里停止处理,除非产生副作用,否则不会产生任何结果。 我发现经常是不是我想做的事,但(因为我喜欢返回物流)。 下一篇文章通过研究如何在不中断管道的情况下当场处理异常来解决此问题。

翻译自: https://www.javacodegeeks.com/2017/02/repackaging-exceptions-streams.html

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

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

相关文章

如何将word中的对象怎么显示到工具栏_MathType怎么添加到Word快速访问栏?

众所周知&#xff0c;MathType是一款应用广泛且功能强大的公式编辑器&#xff0c;特别是应用到Word中&#xff0c;可以对文章中的公式进行批量操作&#xff0c;进行这个公式操作的前提是&#xff0c;需要把MathType添加到Word文档的快速访问栏中&#xff0c;也就是说只安装了Ma…

Java 多线程异常捕获Runnable实现

1、背景&#xff1a; Java 多线程异常不向主线程抛&#xff0c;自己处理&#xff0c;外部捕获不了异常。所以要实现主线程对子线程异常的捕获。 2、工具&#xff1a; 实现Runnable接口的LayerInitTask类&#xff0c;ThreadException类&#xff0c;线程安全的Vector 3、思路&a…

团队项目冲刺第六天

昨日任务  对于关于我界面的 关于我们 的界面。今日任务完成功能介绍界面以及转换&#xff0c;并且在功能介绍界面增加特效&#xff0c;获取本机的时间&#xff0c;年月日星期几小时分钟 秒&#xff0c;可以看时间。明日任务完成联系我们的功能。困难在增加特效功能中一直获…

tf.cast

tf.cast(x, dtype, nameNone) 将x的类型转换为指定类型 import tensorflow as tf import numpy as np znp.array([0,1,3,4,6,4,7,1,0]) cast tf.cast(z,tf.float32)with tf.Session()as sess:print(z)print(sess.run(cast))输出 z的元素有整形变为浮点型

python 输入字符串_输入输出,字符串如影随形 | Python基础连载(五)

更好的排版阅读体验&#xff0c;请移步 微信端 开篇 前面两期详细的介绍了字符串及其相关操作&#xff0c;在今后的编程中&#xff0c;你将经常和字符串打交道。一个比较常见的场景就是输入输出&#xff0c;所以本期将结合字符串&#xff0c;讲述最基本同时也是最常用的输入输出…

linux 环境变量设置方法总结(PATH/LD_LIBRARY_PATH)

PATH和LD_LIBRARY_PATH本质都是变量&#xff0c;所谓变量的意思就是由别人赋值产生的&#xff0c;直觉往往会让我们添加和减少这个变量本身的某些路径&#xff0c;实际上这是不正确的。正确的做法是我们要去修改赋予这个变量数值的那些配置文件&#xff0c;加一条路径或者减一条…

BERT、MT-DNN、GPT2.0、ERNIE

目前效果较好的大部分的nlp任务都会应用预训练语言模型的迁移知识&#xff0c;主要是采用两阶段的模型。第一阶段进行预训练&#xff0c;一般是训练一个语言模型。最出名的是BERT,BERT的预训练阶段包括两个任务&#xff0c;一个是Masked Language Model&#xff0c;还有一个是N…

tf.argmax

tf.argmax可以认为就是np.argmax。tensorflow使用numpy实现的这个API 简单的说&#xff0c;tf.argmax就是返回最大的那个数值所在的下标。 test np.array([[1, 2, 3], [2, 3, 4], [5, 4, 3], [8, 7,2]])np.argmax(test, 0)   &#xff03;输出&#xff1a;array([3, 3, 1]…

guava 并发_Google Guava并发– ListenableFuture

guava 并发在上一篇文章中&#xff0c;我介绍了使用Guava库中com.google.common.util.concurrent包中的Monitor类的方法。 在这篇文章中&#xff0c;我将继续介绍Guava并发实用程序&#xff0c;并讨论ListenableFuture接口。 ListenableFuture通过添加接受完成侦听器的方法&…

mysql 递归_「MySQL」 - SQL Cheat Sheet - 未完成

近几个月的心情真是安排的妥妥的&#xff0c;呈现W状。多的不说了&#xff0c;这里对SQL的测试进行简单梳理&#xff0c;制作一份SQL Cheat Sheet。0x01、数据库基本架构Clinet层Server层连接器网络连接建立、管理长连接导致的OOM的自动处理权限管理缓存KV分析器词法分析语法分…

ThreadPoolExecutor使用和思考(上)-线程池大小设置与BlockingQueue的三种实现区别

前记&#xff1a; jdk官方文档&#xff08;javadoc&#xff09;是学习的最好&#xff0c;最权威的参考。文章分上中下。上篇中主要介绍ThreadPoolExecutor接受任务相关的两方面入参的意义和区别&#xff0c;池大小参数corePoolSize和maximumPoolSize&#xff0c;BlockingQueue选…

Apache Lucene 7.0即将发布!

Apache Lucene项目可能会在几个月后发布其下一个主要版本7.0&#xff01; 请记住&#xff0c;Lucene开发人员通常会努力为下一个非主要&#xff08;功能&#xff09;发行版移植新功能&#xff0c;而即将发布的6.5已经有了很多重大更改 &#xff0c;因此新的主要发行版令人兴奋…

windows常用服务命令

windows运行打开服务命令的方法 &#xff1a;在开始-》运行&#xff0c;输入以下命令 gpedit.msc-----组策略 sndrec32-------录音机 Nslookup-------IP地址侦测器 explorer-------打开资源管理器 logoff---------注销命令 tsshutdn-------60秒倒计时关机命令 explorer--…

springmvc 配置 tag lib_Java自学之springMVC:Hello Spring MVC

学习目的&#xff1a;初识SpringMVC&#xff0c;了解SpringMVC的工作原理Part 1新建一个动态web项目&#xff0c;命名为springMVC&#xff0c;在WEB-INF/lib中&#xff0c;添加所需要的jar包。Part 2在WEB-INF下新建一个web.xml。配置一个DispatcherServlet&#xff0c;所有的请…

GoogLeNet

GoogLeNetInception结构的主要思路是怎样用密集成分来近似最优的局部稀疏结构。对上图做以下说明&#xff1a; 1 . 采用不同大小的卷积核意味着不同大小的感受野&#xff0c;最后拼接意味着不同尺度特征的融合&#xff1b; 2 . 之所以卷积核大小采用1、3和5&#xff0c;主要是为…

1. ThreadPoolExecutor的一个常用的构造方法

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) 参数说明&#xff1a; -corePoolSize       线程池中所保存的核心线程数。线程池启动后默…

spring boot 启动卡住_SpringBoot集成的44种启动器,你知道几种?

点击蓝字“程序员考拉”欢迎关注&#xff01;啥是应用启动器&#xff1f;SpringBoot集成了spring的很多模块&#xff0c;比如tomcat、redis等等。你用SpringBoot搭建项目&#xff0c;只需要在pom.xml引入相关的依赖&#xff0c;和在配置文件中简单的配置就可以使用相应模块了。…

junit 经典示例_JUnit4参数化和理论示例

junit 经典示例我始终依靠TestNG将参数传递给测试方法&#xff0c;以便为我的测试或套件提供一些灵活性。 但是&#xff0c;使用JUnit4可以实现相同的灵活性。 要使用它很简单&#xff1a; package com.marco.test;import java.util.Arrays;import java.util.Collection;imp…

Spring+Hibernate+Atomikos集成构建JTA的分布式事务--解决多数据源跨库事务

一、概念 分布式事务 分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简言之&#xff0c;同时操作多个数据库保持事务的统一&#xff0c;达到跨库事务的效果。 JTA JTA&#xff0c;即Java Transaction API&a…

spring el表达式 if else_Spring 获取单例流程(二)

读完这篇文章你将会收获到Spring 中 prototype 类型的 bean 如何做循环依赖检测Spring 中 singleton 类型的 bean 如何做循环依赖检测前言继上一篇文章 Spring 获取单例流程(一) 我们这次继续往下分析一下后面的流程上一篇文章中我们说到&#xff0c;首先我们根据 name 找到其对…