Java 利用提取的方法实现异常处理

Java 利用提取的方法实现异常处理,lambda 表达式中的代码需要抛出异常,但用户不希望将 lambda 代码块与异常处理的代码混在一起。创建一个单独的方法来执行操作、处理异常,在 lambda 表达式中调用提取的方法。lambda 表达式实际上属于函数式接口中单一抽象方法的实现。与匿名内部类一样,lambda 表达式只能抛出在抽象方法签名中声明的异常。

Java 利用提取的方法实现异常处理 问题描述

lambda 表达式中的代码需要抛出异常,但用户不希望将 lambda 代码块与异常处理的代码混在一起。

Java 利用提取的方法实现异常处理 解决方案

创建一个单独的方法来执行操作、处理异常,在 lambda 表达式中调用提取的方法。

Java 利用提取的方法实现异常处理 具体实例

lambda 表达式实际上属于函数式接口中单一抽象方法的实现。与匿名内部类一样,lambda 表达式只能抛出在抽象方法签名中声明的异常。
如果所需的异常为非受检异常(unchecked exception),则情况相对简单。所有非受检异常均源自 java.lang.RuntimeException10。与其他 Java 代码类似,lambda 表达式可以抛出运行时异常(runtime exception)而不必进行声明或将代码包装在 try/catch 块中,异常随后被传送给调用者。
10这可能是整个 Java API 中最糟糕的命名——既然所有异常均在运行时抛出(否则它们属于编译器错误),那么将这个类命名为 UncheckedException 不是更直接吗?或许是为了凸显这个命名的糟糕,Java 8 引入了一个称为 java.io.UncheckedIOException 的类,以规避本范例讨论的某些问题。

例 5-31 创建了一个名为 div 的方法,它采用常数值对集合中所有元素进行除法操作。

例 5-31 可能抛出非受检异常的 lambda 表达式

public List<Integer> div(List<Integer> values, Integer factor) {
    return values.stream()
        .map(n -> n / factor)  ➊
        .collect(Collectors.toList());
}

➊ 可能抛出 ArithmeticException
对整数除法而言,除数为 0 将抛出 ArithmeticException(非受检异常)11,它将传送给调用者,如例 5-32 所示。
11有趣的是,如果将被除数和除数的类型从 Integer 改为 Double,那么即便除数为 0.0,程序也不会抛出任何异常,而是输出一个所有元素为“无穷大”(Infinity)的结果。无论读者是否相信,根据二进制计算机处理浮点数需要遵循的 IEEE 754 规范,这种操作是合法的(作者在使用 Fortran 语言编程时曾被这种情况搞得十分头疼,花了不少时间才摆脱这个噩梦)。

例 5-32 客户端代码

List<Integer> values = Arrays.asList(30, 10, 40, 10, 50, 90);
List<Integer> scaled = demo.div(values, 10);
System.out.println(scaled);
// 打印[3, 1, 4, 1, 5, 9]

scaled = demo.div(values, 0);
System.out.println(scaled);
// 抛出ArithmeticException(因为除数为0)

客户端代码调用 div 方法,如果除数为 0,lambda 表达式将抛出 ArithmeticException。客户端可以在 map 方法中添加一个 try/catch 块以处理异常,但代码的可读性会因此而变得很差(如例 5-33 所示)。

例 5-33 包含 try/catch 代码块的 lambda 表达式

public List<Integer> div(List<Integer> values, Integer factor) {
    return values.stream()
        .map( n -> {
            try {
                return n / factor;
            } catch (ArithmeticException e) {
                e.printStackTrace();
            }
        })
        .collect(Collectors.toList());
}

只要在函数式接口中声明受检异常(checked exception),就能应用上述过程。
一般来说,应尽量保持流处理代码的简洁,让每个中间操作占据一行。我们可以将 map 方法内部的函数提取到另一个方法以简化代码,然后调用这个方法来完成流处理,如例 5-34 所示。

例 5-34 将 lambda 表达式提取到另一个方法

private Integer divide(Integer value, Integer factor) {
    try {
        return value / factor;
    } catch (ArithmeticException e) { ➊
        e.printStackTrace();
    }
}

public List<Integer> divUsingMethod(List<Integer> values, Integer factor) {
    return values.stream()
        .map(n -> divide(n, factor))  ➋
        .collect(Collectors.toList());
}

❶ 这段代码负责处理异常
❷ 流处理代码得以简化
此外,如果提取的方法不需要 factor 值,则 map 方法的参数可以简化为一个方法引用。
将 lambda 表达式提取到单独的方法同样具有不少优点。我们既可以为提取的方法编写测试(对于私有方法则使用反射),也可以在方法内部设置断点,还可以使用与方法相关的其他机制。

赞(1)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

Java 实例