简体   繁体   English

在lambda中处理lambda中没有try-catch的异常

[英]Handling exception in the lambda without try-catch in the lambda

As far as I know, one could not handle exception thrown in the lambda if the abstract method implemented by the lambda doesn't have throws in its signature. 据我所知,如果lambda实现的抽象方法在其签名中没有throws ,则无法处理lambda中throws

I encountered following code, it works. 我遇到以下代码,它的工作原理。 Why openStream() doesn't demand handling IOException ? 为什么openStream()不要求处理IOException I can see try-catch in the tryWithResources but I don't understand the mechanism behind it. 我可以在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();
        });
    }
}

I've simplified your example to the core part: 我已将您的示例简化为核心部分:

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) { }

If you try with this, you will see that withCallable will compile fine but that withSupplier does not compile; 如果您尝试使用它,您将看到withCallable将编译正常,但withSupplier不编译; even if the lambda expression is compatible with the signature of both functional interfaces. 即使lambda表达式与两个功能接口的签名兼容。

The reason behind this is that the functional method of the Callable interface, which is call() , declares throws Exception in its signature. 这背后的原因是Callable接口的函数方法,即call() ,声明在其签名中throws Exception Supplier.get() does not. Supplier.get()没有。

Quoting the JLS section 11.2.3 : 引用JLS 第11.2.3节

It is a compile-time error if a lambda body can throw some exception class E when E is a checked exception class and E is not a subclass of some class declared in the throws clause of the function type targeted by the lambda expression. 当一个lambda体可以抛出一些异常类E时,如果E是一个经过检查的异常类,并且E不是一个在lambda表达式所针对的函数类型的throws子句中声明的某个类的子类,那么这是一个编译时错误。

As far as I know, one could not handle exception thrown in the lambda if the abstract method implemented by the lambda doesn't have throws in its signature. 据我所知,如果lambda实现的抽象方法在其签名中没有抛出,则无法处理lambda中抛出的异常。

There's a workaround, known as sneaky throw ; 有一种解决方法,称为偷偷摸摸 ; here a simpler example without the functional helpers. 这里是一个没有功能助手的简单例子

By having a few static helper functions that turn your throwing signatures into non-throwing ones which trick the compiler into rethrowing checked exceptions you can turn 通过使用一些静态辅助函数将您的抛出签名转换为非抛出函数,这会使编译器重新抛出已检查的异常,您可以转向

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

into

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

without wrapping the exception into a generic runtime exception. 不将异常包装到通用运行时异常中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM