Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

I encountered following code, it works. Why openStream() doesn't demand handling IOException? I can see try-catch in the tryWithResources but I don't understand the mechanism behind it.

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();
        });
    }
}
like image 202
Yoda Avatar asked Nov 18 '15 09:11

Yoda


1 Answers

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; even if the lambda expression is compatible with the signature of both functional interfaces.

The reason behind this is that the functional method of the Callable interface, which is call(), declares throws Exception in its signature. Supplier.get() does not.

Quoting the JLS section 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.

like image 76
Tunaki Avatar answered Nov 14 '22 22:11

Tunaki