【问题标题】:Handling exception in the lambda without try-catch in the lambda在 lambda 中不使用 try-catch 处理异常
【发布时间】:2015-11-18 09:41:07
【问题描述】:

据我所知,如果 lambda 实现的抽象方法的签名中没有 throws,则无法处理 lambda 中引发的异常。

我遇到了以下代码,它可以工作。为什么openStream() 不需要处理IOException?我可以在tryWithResources 中看到try-catch,但我不明白它背后的机制。

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.function.Function;
import java.util.function.Supplier;

public class Main {

    public static <AUTOCLOSEABLE extends AutoCloseable, OUTPUT> Supplier<OUTPUT> tryWithResources(
            Callable<AUTOCLOSEABLE> callable, Function<AUTOCLOSEABLE, Supplier<OUTPUT>> function,
            Supplier<OUTPUT> defaultSupplier) {
        return () -> {
            try (AUTOCLOSEABLE autoCloseable = callable.call()) {
                return function.apply(autoCloseable).get();
            } catch (Throwable throwable) {
                return defaultSupplier.get();
            }
        };
    }

    public static <INPUT, OUTPUT> Function<INPUT, OUTPUT> function(Supplier<OUTPUT> supplier) {
        return i -> supplier.get();
    }

    public static void main(String... args) {
        Map<String, Collection<String>> anagrams = new ConcurrentSkipListMap<>();
        int count = tryWithResources(
                () -> new BufferedReader(new InputStreamReader(
                        new URL("http://www.puzzlers.org/pub/wordlists/unixdict.txt").openStream())),
                reader -> () -> reader.lines().parallel().mapToInt(word -> {
                    char[] chars = word.toCharArray();
                    Arrays.parallelSort(chars);
                    String key = Arrays.toString(chars);
                    Collection<String> collection = anagrams.computeIfAbsent(key, function(ArrayList::new));
                    collection.add(word);
                    return collection.size();
                }).max().orElse(0), () -> 0).get();
        anagrams.values().stream().filter(ana -> ana.size() >= count).forEach((list) -> {
            for (String s : list)
                System.out.print(s + " ");
            System.out.println();
        });
    }
}

【问题讨论】:

    标签: java lambda exception-handling java-8


    【解决方案1】:

    我已将您的示例简化为核心部分:

    public static void main(String[] args) {
        withCallable(() -> new URL("url").openStream()); // compiles
        withSupplier(() -> new URL("url").openStream()); // does not compile
    }
    
    public static <T> void withCallable(Callable<T> callable) { }
    
    public static <T> void withSupplier(Supplier<T> callable) { }
    

    如果您尝试这样做,您会看到withCallable 可以正常编译,但withSupplier 无法编译;即使 lambda 表达式与两个函数接口的签名兼容。

    这背后的原因是Callable接口的函数方法call()在其签名中声明了throws ExceptionSupplier.get() 没有。

    引用 JLS section 11.2.3:

    如果当 E 是已检查异常类并且 E 不是 lambda 表达式所针对的函数类型的 throws 子句中声明的某个类的子类时,lambda 主体可以抛出某个异常类 E,则这是编译时错误.

    【讨论】:

      【解决方案2】:

      据我所知,如果 lambda 实现的抽象方法在其签名中没有 throws,则无法处理 lambda 中抛出的异常。

      有一种解决方法,称为sneaky throw;这里是 simpler example 没有功能帮助。

      通过使用一些静态辅助函数,将您的抛出签名转换为非抛出签名,从而诱使编译器重新抛出已检查的异常,您可以将其转为

      .stream().map(x -> {
        try {
          return this.throwingFunction(x)
        } catch(Exception e) {
          throw new RuntimeException(e);
        }
      }).forEach(...)
      

      进入

      .stream().map(uncheckedFunc(this::throwingFunction)).forEach(...)

      不将异常包装到通用运行时异常中。

      【讨论】:

      • 但这不是这里的问题。这更像是一条评论。
      猜你喜欢
      • 1970-01-01
      • 2011-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-13
      • 2017-03-04
      • 2011-01-01
      • 1970-01-01
      相关资源
      最近更新 更多