Java 泛型异常包装器的应用,存在一个抛出异常的 lambda 表达式,但用户希望使用泛型包装器(generic wrapper)来捕获所有受检异常,并将它们作为非受检异常重新抛出。创建特殊的异常类(exception class),添加一个接受这些类的泛型方法,并返回不抛出异常的 lambda 表达式。
Java 泛型异常包装器的应用 问题描述
存在一个抛出异常的 lambda 表达式,但用户希望使用泛型包装器(generic wrapper)来捕获所有受检异常,并将它们作为非受检异常重新抛出。
Java 泛型异常包装器的应用 解决方案
创建特殊的异常类(exception class),添加一个接受这些类的泛型方法,并返回不抛出异常的 lambda 表达式。
Java 泛型异常包装器的应用 具体实例
范例利用提取的方法实现异常处理和范例受检异常与lambda表达式讨论了如何委托一个单独的方法来处理 lambda 表达式抛出的异常,不过我们需要为每个可能抛出异常的操作定义一个私有方法。可以采用泛型包装器使其更为通用。
为此,我们定义一个单独的函数式接口,它包含一个声明抛出 Exception
的方法,然后通过某种包装器方法(wrapper method)将其连接到用户代码。
例如,Stream
接口的 map
方法需要一个 Function
,但 Function
接口的 apply
方法不会声明任何受检异常。为了在 map
方法中使用可能抛出受检异常的 lambda 表达式,我们创建一个单独的函数式接口,声明它将抛出 Exception
,如例 5-39 所示。
例 5-39 抛出
Exception
的函数式接口
@FunctionalInterface
public interface FunctionWithException<T, R, E extends Exception> {
R apply(T t) throws E;
}
接下来,在 try/catch 代码块中包装 apply
方法,以添加一个传入 FunctionWithException
并返回 Function
的包装器方法,如例 5-40 所示。
例 5-40 用于处理异常的包装器方法
private static <T, R, E extends Exception>
Function<T, R> wrapper(FunctionWithException<T, R, E> fe) {
return arg -> {
try {
return fe.apply(arg);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
如上所示,wrapper
方法接受抛出 Exception
的代码,并构建必要的 try/catch 块,同时委托给 apply
方法。在本例中,wrapper
方法被声明为 static
,但这并非强制要求。如例 5-41 所示,我们可以使用任何抛出异常的 Function
来调用包装器。
例 5-41 泛型静态包装器方法的应用
public List<String> encodeValuesWithWrapper(String... values) {
return Arrays.stream(values)
.map(wrapper(s -> URLEncoder.encode(s, "UTF-8"))) ➊
.collect(Collectors.toList());
}
➊ 使用 wrapper
方法
接下来,可以在抛出异常的 map
操作中编写代码,wrapper
方法会将其作为未受检异常重新抛出。这种方案的不足之处在于,需要为所有计划使用的函数式接口创建单独的泛型包装器(如 ConsumerWithException
和 SupplierWithException
)。
这未免有些复杂。有鉴于此,不难理解为何某些 Java 框架(如 Spring 和 Hibernate)甚至整个语言(如 Groovy 和 Kotlin)在捕获所有受检异常后,再将它们作为非受检异常重新抛出。