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