2015-11-18 20 views
5

ラムダによって実装された抽象メソッドがその署名にthrowsを持たない場合、私が知る限り、ラムダでスローされた例外を処理できませんでした。ラムダのtry-catchなしでの例外の処理

次のコードが発生しました。なぜopenStream()は処理を要求しませんIOException?私はtryWithResourcestry-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(); 
     }); 
    } 
} 

答えて

8

私は、コア部分にあなたの例を簡略化してきました:

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がコンパイルできないことがわかります。ラムダ式が両方の機能インタフェースのシグネチャと互換性があるとしても。

この理由は、call()というCallableインターフェイスの機能メソッドは、その署名にthrows Exceptionと宣言しているためです。 Supplier.get()はありません。

Eがチェック例外クラスであり、Eがで宣言されたいくつかのクラスのサブクラスではない場合、ラムダ体は、いくつかの例外クラスEを投げることができればそれはコンパイル時のエラーです:JLS section 11.2.3を引用

ラムダ式の対象となる関数型のthrows節。

1

ラムダによって実装抽象メソッドは、その署名に投げていない場合は私の知る限りでは、1はラムダでスローされた例外を処理できませんでした。

回避方法はsneaky throwです。ここでは機能的なヘルパーなしでsimpler exampleです。

再スローへのコンパイラはあなたが

なし

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

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

を回すことができる例外を確認トリック非投げのものにあなたの投げ署名を有効にいくつかの静的なヘルパー機能を有することにより、例外を汎用実行時例外にラップします。

+1

しかし、これは問題ではありません。これはもっとコメントです。 – Tunaki