Java 受检异常与lambda表达式

Java 受检异常与lambda表达式,存在一个抛出受检异常的 lambda 表达式,且所实现的函数式接口中的抽象方法并未声明该异常。为 lambda 表达式添加一个 try/catch 代码块,或委托给某个提取的方法进行处理。lambda 表达式实际上属于函数式接口中单一抽象方法的实现,因此只能抛出在抽象方法签名中声明的受检异常。

Java 受检异常与lambda表达式 问题描述

存在一个抛出受检异常的 lambda 表达式,且所实现的函数式接口中的抽象方法并未声明该异常。

Java 受检异常与lambda表达式 解决方案

为 lambda 表达式添加一个 try/catch 代码块,或委托给某个提取的方法进行处理。

Java 受检异常与lambda表达式 具体实例

lambda 表达式实际上属于函数式接口中单一抽象方法的实现,因此只能抛出在抽象方法签名中声明的受检异常。
假设我们希望通过 URL 调用服务,且需要根据字符串参数的集合构建一个查询字符串,那么参数应该采用可以在 URL 中使用的方式进行编码。为此,Java 提供了一个称为 java.net.URLEncoder 的类(其用途从类名中一目了然),包括一个静态方法 encode,它传入 String 作为参数,并根据指定的编码方式对其进行编码。
我们很容易写出类似例 5-35 所示的代码。

例 5-35 对字符串集合进行 URL 编码(无法编译)

public List<String> encodeValues(String... values) {
    return Arrays.stream(values)
        .map(s -> URLEncoder.encode(s, "UTF-8")))  ➊
        .collect(Collectors.toList());
}

➊ 抛出一个必须处理的异常 UnsupportedEncodingException
encodeValues 方法传入一个字符串的可变参数列表,并根据推荐的 UTF-8 编码方式,尝试采用 UREncoder.encode 方法对每个字符串进行编码。然而,由于 encodeValues 方法抛出受检异常 UnsupportedEncodingException,导致上述代码无法编译。
即便声明 encodeValues 方法抛出该异常,代码仍然无法编译。

例 5-36 声明异常(仍然无法编译)

public List<String> encodeValues(String... values)
    throws UnsupportedEncodingException {  ➊
        return Arrays.stream(values)
            .map(s -> URLEncoder.encode(s, "UTF-8")))
            .collect(Collectors.toList());
}

➊ 声明 encodeValues 方法抛出 UnsupportedEncodingException,但代码仍然无法编译
问题在于,从 lambda 表达式抛出异常就像采用某种方法构建一个完全独立的类,并从中抛出异常。我们可以将 lambda 表达式视为匿名内部类的实现,由此可见,内部对象抛出的异常仍然需要在内部对象而非周围对象(surrounding object)中进行处理或声明。正确的代码如例 5-37 所示,包括匿名内部类和 lambda 表达式两种实现。

例 5-37 利用 try/catch 代码块进行 URL 编码(可以编译)

public List<String> encodeValuesAnonInnerClass(String... values) {
    return Arrays.stream(values)
        .map(new Function<String, String>() {            ➊
            @Override
            public String apply(String s) {                ➋
                try {
                    return URLEncoder.encode(s, "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                    return "";
                }
            }
        })
        .collect(Collectors.toList());
}

public List<String> encodeValues(String... values) {    ➌
    return Arrays.stream(values)
        .map(s -> {
            try {
                return URLEncoder.encode(s, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                return "";
            }
        })
        .collect(Collectors.toList());
}

❶ 匿名内部类实现
❷ 包含将抛出受检异常的代码
❸ lambda 表达式实现
由于 apply 方法(Function 接口包含的单一抽象方法)并未声明任何受检异常,必须在任何实现该方法的 lambda 表达式中添加一个 try/catch 代码块。如果使用 lambda 表达式(如本例所示),我们甚至无法看到 apply 方法签名,遑论对其进行修改(程序也不允许)。
例 5-38 显示了如何利用提取的方法进行编码。

例 5-38 委托给 encodeString 方法进行 URL 编码

private String encodeString(String s) { ➊
    try {
        return URLEncoder.encode(s, "UTF-8");
    } catch (UnsupportedEncodingException e) {
        throw new RuntimeException(e);
    }
}

public List<String> encodeValuesUsingMethod(String... values) {
    return Arrays.stream(values)
        .map(this::encodeString)        ➋
        .collect(Collectors.toList());
}

❶ 用于异常处理的提取方法
❷ 提取方法的方法引用
上述代码有效且易于实现,并提供了一种可以分别进行测试和调试的方法。唯一的不足之处在于,我们需要为每个可能抛出异常的操作提取一个方法。但前面的范例曾经提到过,这使得对流处理的各个组件进行测试更加容易。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程