这个
LambdaExceptionUtil
辅助类使您可以在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 LambdaExceptionUtil {
@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; }
}
在静态导入LambdaExceptionUtil
之后,有许多其他使用它的示例:
@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");
}
2015年11月更新 代码已经得到@PaoloC的帮助改进,请查看他下面的回答并给予赞同。他帮助解决了最后一个问题:现在编译器会要求您添加throw子句,一切都像您可以在Java 8流中本地抛出已检查的异常。
注1
上面LambdaExceptionUtil
类的rethrow
方法可以毫无顾虑地使用,并且可在任何情况下使用。
注2
上面LambdaExceptionUtil
类的uncheck
方法是额外的方法,如果您不想使用它们,则可以安全地将它们从类中删除。如果您确实使用了它们,请小心使用,并在理解以下用例、优缺点和限制之前不要使用:
You may use the uncheck
methods if you are calling a method which literally can never throw the exception that it declares. For example: new String(byteArr, "UTF-8") throws UnsupportedEncodingException, but UTF-8 is guaranteed by the Java spec to always be present. Here, the throws declaration is a nuisance and any solution to silence it with minimal boilerplate is welcome:
String text = uncheck(() -> new String(byteArr, "UTF-8"));
You may use the uncheck
methods if you are implementing a strict interface where you don't have the option for adding a throws declaration, and yet throwing an exception is entirely appropriate. Wrapping an exception just to gain the privilege of throwing it results in a stacktrace with spurious exceptions which contribute no information about what actually went wrong. A good example is Runnable.run()
, which does not throw any checked exceptions.
In any case, if you decide to use the uncheck
methods, be aware of these two consequences of throwing CHECKED exceptions without a throws clause:
- The calling-code won't be able to catch it by name (if you try, the compiler will say: "Exception is never thrown in body of corresponding try statement"). It will bubble and probably be caught in the main program loop by some
catch
Exception
or catch
Throwable
, which may be what you want anyway.
- It violates the principle of least surprise: it will no longer be enough to catch
RuntimeException
to be able to guarantee catching all possible exceptions. For this reason, I believe this should not be done in framework code, but only in business code that you completely control.
参考文献
try-catch
块,但这根本没有任何意义。一旦 lambda 中以某种方式使用了Class.forName
,例如在names.forEach(Class::forName)
中,问题就出现了。基本上,抛出已检查异常的方法因为其设计不当直接被排除在函数式编程作为函数式接口的参与者之外。 - Christian Hujer