这个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 {
void accept(T t) throws E;
}
@FunctionalInterface
public interface BiConsumer_WithExceptions {
void accept(T t, U u) throws E;
}
@FunctionalInterface
public interface Function_WithExceptions {
R apply(T t) throws E;
}
@FunctionalInterface
public interface Supplier_WithExceptions {
T get() throws E;
}
@FunctionalInterface
public interface Runnable_WithExceptions {
void run() throws E;
}
/** .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name)))); or .forEach(rethrowConsumer(ClassNameUtil::println)); */
public static Consumer rethrowConsumer(Consumer_WithExceptions consumer) throws E {
return t -> {
try { consumer.accept(t); }
catch (Exception exception) { throwAsUnchecked(exception); }
};
}
public static BiConsumer rethrowBiConsumer(BiConsumer_WithExceptions biConsumer) throws E {
return (t, u) -> {
try { biConsumer.accept(t, u); }
catch (Exception exception) { throwAsUnchecked(exception); }
};
}
/** .map(rethrowFunction(name -> Class.forName(name))) or .map(rethrowFunction(Class::forName)) */
public static Function rethrowFunction(Function_WithExceptions function) throws E {
return t -> {
try { return function.apply(t); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
};
}
/** rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))), */
public static Supplier rethrowSupplier(Supplier_WithExceptions function) throws E {
return () -> {
try { return function.get(); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
};
}
/** uncheck(() -> Class.forName("xxx")); */
public static void uncheck(Runnable_WithExceptions t)
{
try { t.run(); }
catch (Exception exception) { throwAsUnchecked(exception); }
}
/** uncheck(() -> Class.forName("xxx")); */
public static R uncheck(Supplier_WithExceptions supplier)
{
try { return supplier.get(); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
}
/** uncheck(Class::forName, "xxx"); */
public static R uncheck(Function_WithExceptions function, T t) {
try { return function.apply(t); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
}
@SuppressWarnings ("unchecked")
private static 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 classes1
= Stream.of("Object", "Integer", "String")
.map(rethrowFunction(className -> Class.forName("java.lang." + className)))
.collect(Collectors.toList());
List 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子句添加到包含流的方法声明中(如果不这样做,则会说:异常永远不会在相应的try语句的主体中抛出)。
•在任何情况下,您将无法包围流本身以捕获已检查的异常INSIDE包含流的方法(如果您尝试,编译器将说:异常永远不会在相应的try语句的主体中抛出)。
•如果您正在调用一个字面上永远不会抛出它声明的异常的方法,那么您不应该包含throws子句。例如:new String(byteArr,“UTF-8”)抛出UnsupportedEncodingException,但Java规范保证UTF-8始终存在。在这里,投掷声明是一个麻烦,任何解决方案,以最小的样板沉默它是受欢迎的。
•如果你讨厌检查过的异常并且觉得它们永远不应该被添加到Java语言中(越来越多的人这样想,而我不是其中之一),那么就不要将已检查的异常添加到throws包含流的方法的子句。然后,检查的异常将表现得像UNchecked异常。
•如果您正在实现一个严格的接口,其中您没有添加throws声明的选项,但抛出异常是完全合适的,那么为了获得抛出它的特权而包装异常会导致带有虚假异常的堆栈跟踪没有提供实际出错的信息。一个很好的例子是Runnable.run(),它不会抛出任何已检查的异常。在这种情况下,您可以决定不将已检查的异常添加到包含该流的方法的throws子句中。
•在任何情况下,如果您决定不将(但忘记添加)已检查的异常添加到包含流的方法的throws子句中,请注意抛出CHECKED异常的这两个后果:
1)调用代码将无法通过名称捕获它(如果您尝试,编译器将说:异常永远不会在相应的try语句的主体中抛出)。它会冒泡并可能在主程序循环中被一些“捕获异常”或“捕获Throwable”捕获,这可能是你想要的。
2)它违反了最少惊喜的原则:捕获RuntimeException以便能够保证捕获所有可能的异常将不再足够。出于这个原因,我认为这不应该在框架代码中完成,而只能在您完全控制的业务代码中完成。
总结:我认为这里的局限性并不严重,UtilException可以毫不畏惧地使用这门课程。但是,这取决于你!