这个UtilException
帮助类允许您在Java流中使用任何已检查的异常,例如:
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(rethrowFunction(Class::forName))
.collect(Collectors.toList());
注意:Class::forName
抛出 ClassNotFoundException
,这是一个已检查的异常。流本身也会抛出 ClassNotFoundException
,而不是一些包装未经检查的异常。
public final class UtilException {
@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
void accept(T t) throws E;
}
@FunctionalInterface
public interface BiConsumer_WithExceptions<T, U, E extends Exception> {
void accept(T t, U u) throws E;
}
@FunctionalInterface
public interface Function_WithExceptions<T, R, E extends Exception> {
R apply(T t) throws E;
}
@FunctionalInterface
public interface Supplier_WithExceptions<T, E extends Exception> {
T get() throws E;
}
@FunctionalInterface
public interface Runnable_WithExceptions<E extends Exception> {
void run() throws E;
}
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E {
return t -> {
try { consumer.accept(t); }
catch (Exception exception) { throwAsUnchecked(exception); }
};
}
public static <T, U, E extends Exception> BiConsumer<T, U> rethrowBiConsumer(BiConsumer_WithExceptions<T, U, E> biConsumer) throws E {
return (t, u) -> {
try { biConsumer.accept(t, u); }
catch (Exception exception) { throwAsUnchecked(exception); }
};
}
public static <T, R, E extends Exception> Function<T, R> rethrowFunction(Function_WithExceptions<T, R, E> function) throws E {
return t -> {
try { return function.apply(t); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
};
}
public static <T, E extends Exception> Supplier<T> rethrowSupplier(Supplier_WithExceptions<T, E> function) throws E {
return () -> {
try { return function.get(); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
};
}
public static void uncheck(Runnable_WithExceptions t)
{
try { t.run(); }
catch (Exception exception) { throwAsUnchecked(exception); }
}
public static <R, E extends Exception> R uncheck(Supplier_WithExceptions<R, E> supplier)
{
try { return supplier.get(); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
}
public static <T, R, E extends Exception> R uncheck(Function_WithExceptions<T, R, E> function, T t) {
try { return function.apply(t); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
}
@SuppressWarnings ("unchecked")
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E { throw (E)exception; }
}
许多其他使用它的示例(在静态导入
UtilException
之后):
@Test
public void test_Consumer_with_checked_exceptions() throws IllegalAccessException {
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.forEach(rethrowConsumer(className -> System.out.println(Class.forName(className))));
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.forEach(rethrowConsumer(System.out::println));
}
@Test
public void test_Function_with_checked_exceptions() throws ClassNotFoundException {
List<Class> classes1
= Stream.of("Object", "Integer", "String")
.map(rethrowFunction(className -> Class.forName("java.lang." + className)))
.collect(Collectors.toList());
List<Class> classes2
= Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(rethrowFunction(Class::forName))
.collect(Collectors.toList());
}
@Test
public void test_Supplier_with_checked_exceptions() throws ClassNotFoundException {
Collector.of(
rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))),
StringJoiner::add, StringJoiner::merge, StringJoiner::toString);
}
@Test
public void test_uncheck_exception_thrown_by_method() {
Class clazz1 = uncheck(() -> Class.forName("java.lang.String"));
Class clazz2 = uncheck(Class::forName, "java.lang.String");
}
@Test (expected = ClassNotFoundException.class)
public void test_if_correct_exception_is_still_thrown_by_method() {
Class clazz3 = uncheck(Class::forName, "INVALID");
}
但在使用之前,必须了解以下优点、缺点和限制:
• 如果调用代码要处理已检查的异常,则必须将其添加到包含流的方法的throws子句中。编译器不再强制你添加它,所以很容易忘记它。
• 如果调用代码已经处理了已检查的异常,则编译器将提醒你将throws子句添加到包含流的方法声明中(如果不这样做,它会说:Exception is never thrown in body of corresponding try statement)。
• 无论如何,你都无法在包含流的方法内部捕获已检查的异常(如果尝试这样做,编译器将说:Exception is never thrown in body of corresponding try statement)。
• 如果你调用的方法永远不会抛出它声明的异常,则不应该包括throws子句。例如:new String(byteArr, "UTF-8") throws UnsupportedEncodingException,但是根据Java规范,UTF-8始终存在。在这种情况下,throws声明是一个麻烦,并且任何能够以最少的样板代码使其静音的解决方案都受到欢迎。
• 如果你讨厌已检查的异常并认为它们根本不应该被添加到Java语言中(越来越多的人持有这种观点,而我不是其中之一),那么就不要将已检查的异常添加到包含流的方法的throws子句中。然后,已检查的异常将像未检查的异常一样行事。
• 如果你正在实现一个严格的接口,在这个接口中你没有添加throws声明的选项,但是抛出异常是完全适当的,那么仅为了获得抛出它的特权而包装异常会导致带有虚假异常的堆栈跟踪,这些异常对于实际发生的情况没有任何信息贡献。一个很好的例子是Runnable.run(),它不会抛出任何已检查的异常。在这种情况下,你可以决定不将已检查的异常添加到包含流的方法的throws子句中。
• 无论如何,如果你决定不将(或忘记将)已检查的异常添加到包含流的方法的throws子句中,请注意抛出已检查的异常的这两个后果:
1) 调用代码将无法按名称捕获它(如果尝试这样做,编译器将说:Exception is never thrown in body of corresponding try statement)。它将冒泡并可能被某个“catch Exception”或“catch Throwable”在主程序循环中捕获,这也许正是你想要的。
2) 它违反了最小惊奇原则:仅捕获RuntimeException将不再足以保证捕获所有可能的异常。出于这个原因,我认为这不应该在框架代码中完成,而只能在完全由你控制的业务代码中完成。
总之:我认为这里的限制并不严重,可以毫不担心地使用UtilException类。但是,这取决于你!