每个应用程序都生活在现实世界中,而现实世界并不完美。因此,即使是理想的,无错误的应用程序也注定会不时地处理错误。
自从第一个计算机程序诞生以来,问题就一直存在,软件工程师发明了许多错误处理方法!
Java传统上使用以下方法向调用者发出信号,指出存在错误:
- 返回一个特殊值(通常,为此目的使用“ null”值)
- 引发异常
这两种方法都有明显的缺点。
返回一个特殊值将丢弃有关错误实际原因的信息,并通过附加检查使代码膨胀。
与正常的执行流程相比,异常的代价非常高,并且使流程难以遵循且难以验证其正确性。一些库和框架倾向于滥用异常,以使其成为正常执行流程的一部分,这是很疯狂的。
因此,有没有其他方法可以通知呼叫者有关错误而又没有上述缺点?是! 函数式编程提供了一种。
请注意,在下文中,我将尽量避免使用FP专用术语。这不会降低方法的功能性,但是可以为那些还不习惯FP lang语的人简化概念。
该Either<L, R>
集装箱
这个想法是使用容器作为返回值,而不是普通值。容器很特殊:虽然被声明为两种类型,但实际上在第一或第二种类型中,每次仅保存一个值。
的Either<L, R>
是通用的容器,不依赖于误差传播/处理。当将其用于错误传播时,按照约定,第一种(或“左”)类型用于表示错误类型,而第二种(或“右”)类型表示返回值类型。
在代码中,这看起来像:
实际上,与通常的“做某事并成功返回结果,如果有错误则抛出异常”并没有什么不同。
更深入的外观具有很多优点:
- 不再需要返回一些“特殊”值。
- 有关该错误的信息仍然可用。
- 执行流程没有中断。
上面的代码演示了“生产”方面。现在,让我们看一下“消费”方面的样子:
此可疑简单代码包含处理错误所需的一切:
- 如果任何处理步骤返回错误,它将返回正确的错误结果。
- 一旦发生错误,它将立即停止处理。
- 它不会中断执行流程, return语句将始终执行,并且始终将值返回给调用方。
- 它强制执行“处理错误或传播错误”策略,从而产生健壮的代码。
- 这种方法的一致应用会产生清晰易读的代码。
专门研究狭窄的用例
可能会注意到,plain Either<L, R>
用于错误处理时非常冗长。
首先,它要求明确引用错误类型。尽管通常错误的基本类型并不多。例如,Java使用单一Throwable
类型作为所有错误和异常的基类。
冗长和不便的第二个来源(出于此特定目的)是Either<L, R>
一般意义上的意义,即它可以用于任何类型,并且其API在双方方面都是对称的。当Either<L, R>
用于错误处理时,这要求某些约定的一致应用,例如上面提到的约定。
因此,对于狭义的错误处理,Either<L, R>
可以将其专门化为Result<T>
类型,该类型假定单个通用的错误基本类型,并已针对错误处理调整了API。这使代码不再那么冗长,也更容易发生意外错误。
使用Result<T>
,上面的代码可以重写为以下代码:
现在,代码不再那么冗长,而上面提到的所有属性仍然存在。
修改现有代码以供使用 Result<T>
使用的Result<T>
是在自己的代码方便,但我们生活在Java库和框架,不使用它的世界。他们抛出异常并返回空值。因此,我们需要一种方便的方法来与现有代码进行交互。
为此,Reactive Toolbox Core中的 Result<T>
实现提供了一组帮助程序方法,该方法允许将传统方法包装到返回Result的方法中。
下面的示例显示如何使用这些辅助方法:
收尾:
本文试图描述Reactive Toolbox Core库的一些主要概念 。当然,这些概念都不是新概念。我只是试图创建一个库,以方便,一致地应用这些概念。
我经常看到整篇关于“ Java太老了,应该退休并用现代语言代替”的文章。上面提到的概念表明,事实并非如此。在现有的Java功能中,可以编写现代,干净且可靠的代码。改变习惯和方法,而不是语言,一切都是必要的。有趣的是,由于适用于一种以上语言的方法,变更方法比变更语言要付出更多的代价。
有什么问题可以加下qq:2062583349。也可添加vx:admindesire,有java、python、web等习资料和视频课程干货”。欢迎交流!