【问题标题】:Exception handling with Consumer functions in Java 8Java 8 中消费者函数的异常处理
【发布时间】:2016-02-16 21:17:17
【问题描述】:

这段代码在processBatch(batch, this::backupMetacard); 行给我一个编译错误。处理批处理方法将使用者包装在一个try/catch 块中,但Java 不会编译该调用。

private synchronized void drain() {

    for (List<Metacard> batch : Lists.partition(metacards, BATCH_SIZE)) {
        getExecutor().submit(() -> {

            processBatch(batch, this::backupMetacard);
        });
    }
    metacards.clear();
}

void processBatch(List<Metacard> metacards, Consumer<Metacard> operation) {

    List<String> errors = new ArrayList<>();
    for (Metacard metacard : metacards) {
        try {
            operation.accept(metacard);
        } catch (IOException e) {
            errors.add(metacard.getId());
        }
    }

    if (!errors.isEmpty()) {
        LOGGER.info("Plugin processing failed. This is allowable. Skipping to next plugin.",
                pluginExceptionWith(errors));
    }
}

private void backupMetacard(Metacard metacard) throws IOException {...}

【问题讨论】:

  • try-catch 部分在哪里?
  • @Tunaki try{} 将 operation.accept 调用包装在 processBatch 方法中。
  • 一个可能的问题是函数签名没有声明它会引发错误。也许替换为void processBatch(List&lt;Metacard&gt; metacards, Consumer&lt;Metacard&gt; operation) throws IOException()

标签: java exception lambda


【解决方案1】:

问题是在下面的sn-p中,方法backupMetacard声明抛出检查的IOException

getExecutor().submit(() -> {
    processBatch(batch, this::backupMetacard);
                        ^^^^^^^^^^^^^^^^^^^^ // <-- this throws a checked exception
});

因此,它不再符合Consumer的函数方法的约定,即apply,并且没有声明抛出已检查的异常。

将其包装成try-catch,您可以在其中抛出未经检查的异常而不是UncheckedIOException

getExecutor().submit(() -> {
    processBatch(batch, metacard -> {
        try {
            backupMetacard(metacard);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    });
});

【讨论】:

  • 它有效。我希望在 processBatch 方法中保留错误处理,因为这样我就可以多态地使用 backupMetacard()、updateMetacard()、deleteMetacard() 等。但这不会那么容易发生。
【解决方案2】:

Consumer.accept() 没有声明异常,而您的 backupMetacard 方法声明了异常,因此您不能将 this::backupMetacard 作为 Consumer 参数传递。

【讨论】:

    【解决方案3】:

    你可以有一个适配器功能接口

    interface ConsumerX<T>
    {
        void consumeX(T) throws Exception;
    
        void default consume(T t)
        {
            try{ consumeX(t); }
            catch.... // handle exception
        }
    }
    

    并像使用它

    processBatch( batch, (ConsumerX<Metacard>)this::backupMetacard )
    

    类型参数&lt;Metacard&gt; 似乎是多余的,不幸的是在当前的Java 中需要它。然而,我们可以有一个辅助方法来代替

        static <T> ConsumerX<T> of(ConsumerX<T> c){ return c; }
    
    processBatch( batch, ConsumerX.of(this::backupMetacard) )
    

    还有更多的事情需要考虑。目前,ConsumerX 抛出一个固定的、超出范围的Exception。我们宁愿让它抛出与 lambda 主体抛出的相同的异常,即异常透明性。这可以通过consumeX() 抛出一个类型变量来完成。

    另一件事是提供自定义异常处理,例如

        ConsumerX.of( lambda, ex->{ ... } )
    

    或者我喜欢的语法--

        ConsumerX.of(...).catch_(FooException.class, fe->{ ... });
    

    【讨论】:

      【解决方案4】:

      下面是sn-p代码,基本上是包装了try-catch块并被消费的Exception对象。

      public static <T> T unchecked(final ExceptionBearingAction<T> template, Consumer<Exception> exceptionConsumer) {
          T results = null;
          try {
              results = template.doAction();
          } catch (Exception ex) {
              exceptionConsumer.accept(ex);
          }
          return results;
      }
      

      ExceptionBearingAction.Java - 它是一个执行和承载异常操作的功能接口。

      @FunctionalInterface
      public interface ExceptionBearingAction<T> {
      
         T doAction() throws Exception; 
      }
      

      如何使用它

      unchecked(() -> Files.copy(srcPath, Paths.get(distFileUrl), StandardCopyOption.REPLACE_EXISTING), (ex) -> LOGGER.warn("Oops!! copy failed due to {}", ex));
      

      【讨论】:

        【解决方案5】:

        您可以使用 apache commons-lang3 库来做到这一点。

        https://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/function/Failable.html

            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.12.0</version>
            </dependency>
        

        更改方法:processBatch

        void processBatch(List<Metacard> metacards, FailableConsumer<Metacard, IOException> operation) {
        
            List<String> errors = new ArrayList<>();
            for (Metacard metacard : metacards) {
                try {
                    operation.accept(metacard);
                } catch (IOException e) {
                    errors.add(metacard.getId());
                }
            }
        
            if (!errors.isEmpty()) {
                LOGGER.info("Plugin processing failed. This is allowable. Skipping to next plugin.",
                        pluginExceptionWith(errors));
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-01-31
          • 2015-08-15
          • 2012-07-07
          相关资源
          最近更新 更多