Lambda exception handling without trial fishing in lambda

As far as I know, it was impossible to handle the exception thrown in lambda if the abstract method implemented by the lambda does not have throws in its signature.

I came across the following code, it works. Why doesn't openStream() require an IOException ? I see try-catch in tryWithResources , but I don't understand its mechanism.

 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(); }); } } 
+5
source share
2 answers

I simplified your example to the main 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 withSupplier will not compile; even if the lambda expression is compatible with the signature of both functional interfaces.

The reason for this is because the Callable interface functional method, which call() , declares a throws Exception in its signature. Supplier.get() no.

JLS quote section 11.2.3 :

This is a compile-time error if the lambda body can throw some exception class E when E is a proven exception class and E is not a subclass of some class declared in the throws clause of the type of function referenced by the lambda expression,

+8
source

As far as I know, it was impossible to handle the exception thrown in lambda if the abstract method implemented by the lambda does not have throws in its signature.

There is a workaround known as a hidden throw ; here is a simpler example without functional helpers.

With a few static helper functions that turn your metal signatures into non-metal ones that trick the compiler to iterate over checked exceptions, you can turn

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

in

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

without exception, exceptions in the general exception of the runtime

+1
source

Source: https://habr.com/ru/post/1236218/


All Articles