CUBは子供の白熊

Java SE 8 実践プログラミングの練習問題を解く

第1章 ラムダ式とは : 問題 6 : ラムダ式でチェック例外を隠す

問題 6

Runnablerunメソッドはチェック例外をスローできないため、ラムダ式で簡潔に表すには不便なことが多い。
例えば、以下はコンパイルエラーになってしまう。

new Thread(() -> {
    System.out.println("Zzz");
    Thread.sleep(1000);        //← InterruptedException の可能性
}).start();

全てのチェック例外をキャッチしてチェックされない例外に変更するuncheckメソッドを書け

解答

まずRunnableに代わり、例外をスローするrunメソッドを持つ関数型インターフェースを定義する。

@FunctionalInterface
public interface RunnableWithException {
    public void run() throws Exception;
}

uncheckメソッドは、次のようになる。

public static Runnable uncheck(RunnableWithException func) {
    return new Runnable() {
        public void run() {
            try {
                func.run();
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        }
    };
}

ここでラムダ式を使って書くと、uncheckメソッドはより簡潔になる。

public static Runnable uncheck(RunnableWithException func) {
    return () -> {
        try {
            func.run();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    };
}

問題文のコードにuncheckメソッドを適用すると、コンパイルエラーが解消する。

new Thread(uncheck(() -> {
    System.out.println("Zzz");
    Thread.sleep(1000);
})).start();

さらに問題

uncheckメソッドの引数にCallable<Void>が使用できないのはなぜか?

解答

関数型インターフェースRunnableWithExceptionを導入しなくても、uncheck(Callable<Void>)メソッドは書ける。
でもCallable<Void>だと、最後に "return null;" を記述しないとコンパイルエラーになってしまうからです。

この問題だと

new Thread(uncheck(() -> {
    System.out.println("Zzz");
    Thread.sleep(1000);
    return null;        //← この行が余計!
})).start();

というふうに、余計なreturn文が必要になる。

これはVoidクラスが

実体のないvoidキーワードを表す Pseudo-Type

だからです。
JavaGenerics の中途半端な面を見た気がします。