Java从一开始就支持检查异常。 在Java 8中,语言元素lambda和支持流操作的RT库修改将功能编程风格引入了该语言。 函数样式和异常并不是真正的好朋友。 在本文中,我将描述一个简单的库,该库在某种程度上类似于使用Optional
处理null
方式处理异常。
该库有效(毕竟它是单个类和一些内部类,但实际上不是很多)。 另一方面,我不是绝对确定使用该库不会降低普通程序员的编程风格。 可能会有人用锤子把所有东西都看成是钉子。 锤子不是很好的修脚工具。 看看这个库更像是一个想法,而不是作为一个告诉您如何创建完美的代码处理异常的最终工具。
处理检查的异常
已检查的异常必须像感冒一样被声明或捕获。 这是与null
的主要区别。 评估表达式可以静默为null
但不能静默引发已检查的异常。 当结果为null
我们可以使用它来表示没有值,或者我们可以检查并使用“默认”值代替null
。 这样做的代码模式是
var x = expression; if ( expression == null ){
x = expression that is really never null default expression that is really never }
模式表达式是相同的,尽管Java语法略有不同,但表达式的求值可能会引发检查异常。
Type x; // you cannot use 'var' here try {
x = expression } catch (Exception weHardlyEverUseThisValue){
x = expression that does not throw exception default expression that does not }
如果第二个表达式也可以为null
或可能引发异常,并且如果第一个表达式失败,我们需要第三个表达式甚至更多个表达式进行评估,则结构可能会更复杂。 由于许多括号,在抛出异常的情况下,这尤其顽皮
Type x; // you cannot use 'var' here try {
try {
x = expression1
} catch (Exception e){
try {
x = expression2
} catch (Exception e){
try {
x = expression3
} catch (Exception e){
x = expression4
}}}} catch (Exception e){
x = expression that does not throw exception default expression that does not }
对于null
处理,我们有Optional
。 解决百万美元的问题并不是完美的,这是设计一种既没null
又被低估的语言的名称,但是如果使用得当,它会使生活变得更好。 (更糟糕的是,如果使用错误的方式,您可以随意地说,我在本文中所描述的正是这种方式。)
如果结果表达式为null
,则可以编写
var x = Optional.ofNullable(expresssion)
.orElse( expression that does not throw exception); default expression that does not exception);
你也可以写
var x = Optional.ofNullable(expresssion1) .or( () -> Optional.ofNullable(expression2)) .or( () -> Optional.ofNullable(expression3)) .or( () -> Optional.ofNullable(expression4)) ... .orElse( expression that does not throw exception); default expression that does not exception);
当您有很多替代值时。 但是,如果表达式引发异常,则您不能做同样的事情。 可以吗
极好的
库Exceptional
( https://github.com/verhas/exceptional )
< groupId >com.javax0</ groupId > < artifactId >exceptional</ artifactId > < version >1.0.0</ version >
实现了在Optional
实现的所有方法,一个或多个实现了某些方法,并且某些方法的目的有所不同,旨在在异常情况下使用相同的方式,如上面针对null
值的Optional
。
您可以使用Exceptional.of()
或Exceptional.ofNullable()
创建一个Exceptional
值。 重要的区别在于,论点不是价值,而是提供价值的供应商。 该供应商不是JDK Supplier
因为该Supplier
无法引发异常,因此整个库将无用。 此供应商必须是Exceptional.ThrowingSupplier
,它与JDK Supplier
完全相同,但方法get()
可能会抛出Exception
。 (另请注意,只有一个Exception
,而不是Throwable
正如你用裸手搭上了烧红的铁球,你应该只捕捉尽可能频繁。)
在这种情况下,您可以写的是
var x = Exceptional.of(() -> expression) // you CAN use 'var' here
.orElse( expression that does not throw exception); default expression that does not exception);
它越来越短,通常更容易阅读。 (或者不是?这就是为什么APL如此受欢迎?或者是?您问什么是APL?)
如果您有多种选择,可以写
var x = Exceptional.of(() -> expression1) // you CAN use 'var' here
.or(() -> expression2)
.or(() -> expression3) // these are also ThrowingSupplier expressions
.or(() -> expression4) ...
.orElse( expression that does not throw exception); default expression that does not exception);
如果某些供应商可能会导致null
不仅引发异常,则有方法的ofNullable()
和orNullable()
变体。 ( orNullable()
在Optional
中不存在,但在这里,如果整个库都可以使用,则是有意义的。)
如果您熟悉Optional
并使用更高级的方法,如ifPresent()
, ifPresentOrElse()
或orElseThrow()
, stream()
, map()
, flatMap()
, filter()
那么使用Exceptional
并不困难。 类中存在具有相同名称的类似方法。 再次不同的是,如果Optional
的方法的参数为Function
,则为Exceptional
时为ThrowingFunction
。 利用这种可能性,您可以编写如下代码
private int getEvenAfterOdd( int i) throws Exception {
if ( i % 2 == 0 ){
throw new Exception();
}
return 1 ;
}
@Test
@DisplayName ( "some odd example" )
void testToString() {
Assertions.assertEquals( "1" ,
Exceptional.of(() -> getEvenAfterOdd( 1 ))
.map(i -> getEvenAfterOdd(i+ 1 ))
.or( () -> getEvenAfterOdd( 1 ))
.map(i -> i.toString()).orElse( "something" )
);
}
也可以像下面的示例一样处理函数表达式中的异常:
private int getEvenAfterOdd( int i) throws Exception {
if (i % 2 == 0 ) {
throw new Exception();
}
return 1 ;
}
@Test
void avoidExceptionsForSuppliers() {
Assertions.assertEquals( 14 ,
( int ) Optional.of( ).map(i -> 13 ).map(i ->
Exceptional.of(() -> inc(i))
.orElse( 0 )).orElse( 15 ));
}
最后但并非最不重要的一点是,您可以模仿?.
Groovy写作的运营商
abcdef
表达式(其中所有变量/字段都可能为null
并通过它们访问下一个字段)会导致NPE。 您可以但是写
var x = Exceptional.ofNullable( () -> abcdef).orElse( null );
摘要
记住我对锤子说的话。 小心使用,并获得更大的利益。
翻译自: https://www.javacodegeeks.com/2019/05/handling-exceptions-functional-style.html