Java:Stream/Optionalで検査例外/チェック例外を扱う方法
※結論だけ見たい人は、LambdaExceptionUtilの使い方を参照のこと
問題
Stream/Optionalは非常に便利なのだが、ウェブ検索してみると、どこでも困っていることは、処理内容に検査例外/チェック例外のある処理を入れられないことだ。つまり、以下のような処理である。
このままでは、コンパイルできない。IOExceptionが発生するにも関わらず、それに対応していないからだ。
import java.io.*;
import java.nio.file.*;
import java.nio.file.Files;
import java.util.*;
public class Sample {
public static void main(String[]args) throws IOException {
List<String>list = new ArrayList<>();
Arrays.stream(new String[] { "foo.txt", "bar.txt" }).forEach(name-> {
Files.write(Paths.get(name), list); // IOExceptionが発生
});
}
}
対応策:非チェック例外にしてしまう
いったん非チェック例外にラップし、外側で元の例外を取り出すという方法がある。
これは考えただけでも非常に面倒。
import java.io.*;
import java.nio.file.*;
import java.nio.file.Files;
import java.util.*;
public class Sample {
public static void main(String[]args) throws IOException {
List<String>list = new ArrayList<>();
try {
Arrays.stream(new String[] { "foo.txt", "bar.txt" }).forEach(name-> {
try {
Files.write(Paths.get(name), list);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
});
} catch (RuntimeException ex) {
throw (IOException)ex.getCause();
}
}
}
外側で取り出すのをやめたらどうか?非チェック例外だけにしてしまうのである。
import java.io.*;
import java.nio.file.*;
import java.nio.file.Files;
import java.util.*;
public class Sample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Arrays.stream(new String[] { "foo.txt", "bar.txt" }).forEach(name -> {
try {
Files.write(Paths.get(name), list);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
});
}
}
これでも行けそうな気がするが、しかし面倒なことには変わらない。
LambdaExceptionUtilを使う方法
これは以下に紹介されていた。
以下では、Consumerの場合だけに特化した非常に単純化したものを紹介する
import static somePackage.LambdaExceptionUtil.*;
import java.io.*;
import java.nio.file.*;
import java.util.*;
public class Sample {
public static void main(String[] args) throws IOException {
List<String> list = new ArrayList<>();
Arrays.stream(new String[] { "foo.txt", "bar.txt" }).forEach(rethrowConsumer(name -> {
Files.write(Paths.get(name), list);
}));
}
}
public final class LambdaExceptionUtil {
@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
void accept(T t) throws E;
}
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E {
return t -> {
try {
consumer.accept(t);
} catch (Exception exception) {
throwAsUnchecked(exception);
}
};
}
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E {
throw (E) exception;
}
}
この技は自分では編み出せないだろう、脱帽だ。Function, Supplier, Runnable等の場合については、先の投稿を見てほしい。
※さらに、LambdaExceptionUtilを使うと、検査例外を非検査例外として投げることもできる。Java:検査例外(チェック例外)を非検査例外(非チェック例外)として送出するを参照のこと。
ディスカッション
コメント一覧
まだ、コメントがありません