为了账号安全,请及时绑定邮箱和手机立即绑定

如何从Java 8流中抛出检查过的异常?

如何从Java 8流中抛出检查过的异常?

呼啦一阵风 2019-07-04 12:37:25
如何从Java 8流中抛出检查过的异常?如何从Java 8流/lambda中抛出检查过的异常?换句话说,我希望像这样编译代码:public List<Class> getClasses() throws ClassNotFoundException {          List<Class> classes =          Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")               .map(className -> Class.forName(className))               .collect(Collectors.toList());                       return classes;     }此代码不编译,因为Class.forName()以上抛出方法ClassNotFoundException,这是检查过的。请注意,我不希望将选中的异常包装在运行时异常中,而是抛出已包装的未检查异常。我想抛出检查过的异常本身。,而不加丑陋try/catches去小溪。
查看完整描述

3 回答

?
慕的地10843

TA贡献1785条经验 获得超8个赞

这,这个LambdaExceptionUtilHelper类允许您使用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 {@FunctionalInterfacepublic interface Consumer_WithExceptions<T, E extends Exception> {
    void accept(T t) throws E;
    }@FunctionalInterfacepublic interface BiConsumer_WithExceptions<T, U, E extends Exception> {
    void accept(T t, U u) throws E;
    }@FunctionalInterfacepublic interface Function_WithExceptions<T, R, E extends Exception> {
    R apply(T t) throws E;
    }@FunctionalInterfacepublic interface Supplier_WithExceptions<T, E extends Exception> {
    T get() throws E;
    }@FunctionalInterfacepublic interface Runnable_WithExceptions<E extends Exception> {
    void run() throws E;
    }/** .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name)))); or .
    forEach(rethrowConsumer(ClassNameUtil::println)); */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); }
        };
    }/** .map(rethrowFunction(name -> Class.forName(name))) or .map(rethrowFunction(Class::forName)) 
    */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; }
        };
    }/** rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))), 
    */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; }
        };
    }/** 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, E extends Exception> R uncheck(Supplier_WithExceptions<R, E> supplier)
    {
    try { return supplier.get(); }
    catch (Exception exception) { throwAsUnchecked(exception); return null; }
    }/** uncheck(Class::forName, "xxx"); 
    */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):

@Testpublic 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));
    }@Testpublic 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());
    }@Testpublic 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");
    }

附注1:这个rethrow方法LambdaExceptionUtil上面的类可以毫无畏惧地使用,并且是可以在任何情况下使用..这在很大程度上要感谢用户@PaoloC,他帮助解决了最后一个问题:现在编译器将要求您添加抛出子句,这一切就好像您可以在Java 8流上本地抛出检查异常一样。


附注2:这个uncheck方法LambdaExceptionUtil上面的类是额外的方法,如果您不想使用它们,可以将它们安全地从类中删除。如果您确实使用了它们,请谨慎使用它们,而不是在了解以下用例、优点/缺点和限制之前:

·您可以使用uncheck方法,如果您正在调用一个方法,该方法实际上永远不能抛出它声明的异常。例如:新字符串(byteArr,“UTF-8”)抛出UnsuptedEncodingException,但是Java规范保证UTF-8始终存在。在这里,“抛出”声明是一个讨厌的问题,任何用最少的样板使其保持沉默的解决方案都是受欢迎的:String text = uncheck(() -> new String(byteArr, "UTF-8"));

·您可以使用uncheck方法,如果您正在实现一个严格的接口,而您没有添加抛出声明的选项,那么抛出异常是完全合适的。包装一个异常只是为了获得抛出它的特权,结果会产生带有虚假异常的堆栈跟踪,这些异常不会提供关于实际出错的信息。一个很好的例子是Runnable.run(),它不会抛出任何检查过的异常。

·无论如何,如果您决定使用uncheck方法,请注意抛出检查过的异常而没有抛出子句的两个后果:1)调用代码将无法按名称捕获它(如果您尝试,编译器会说:异常永远不会抛出在相应的try语句中)。它会出现气泡,并且可能会被某些“捕捉异常”或“捕捉Throwable”捕获,这可能是您想要的。2)这违反了“最不吃惊”的原则:它将不再足以抓住RuntimeException才能保证捕捉所有可能的异常。因此,我认为这不应该在框架代码中完成,而应该只在您完全控制的业务代码中完成。


查看完整回答
反对 回复 2019-07-04
?
慕桂英4014372

TA贡献1871条经验 获得超13个赞

你不能安全地这么做。你可以作弊,但你的程序被破坏了,这不可避免地会回来咬人(应该是你,但我们的欺骗往往会对别人造成打击)。

这里有一个稍微安全的方法(但我还是不推荐这样做)。

class WrappedException extends RuntimeException {
    Throwable cause;

    WrappedException(Throwable cause) { this.cause = cause; }}static WrappedException throwWrapped(Throwable t) {
    throw new WrappedException(t);}try 
    source.stream()
          .filter(e -> { ... try { ... } catch (IOException e) { throwWrapped(e); } ... })
          ...}catch (WrappedException w) {
    throw (IOException) w.cause;}

在这里,您要做的是捕获lambda中的异常,从流管道中抛出一个信号,该信号表示计算异常失败,捕获信号,并对该信号执行操作以抛出潜在的异常。关键是,您总是捕获合成异常,而不是允许检查的异常泄漏而不声明该异常被抛出。


查看完整回答
反对 回复 2019-07-04
  • 3 回答
  • 0 关注
  • 1942 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信