LambdaExceptionUtilの使い方
これについては、Java:Stream/Optionalで検査例外/チェック例外を扱う方法およびJava:検査例外(チェック例外)を非検査例外(非チェック例外)として送出するでも書いたのだが、改めてその使い方をまとめてみる。
LambdaExceptionUtilの目的
LambdaExceptionUtilによって以下が可能になる。
- StreamやOptional中のラムダ式の中に、簡単に「検査例外(チェック例外)」を生ずる処理を入れ込み、その外側にもとの検査例外を再発生させることができる。
- そもそも「検査例外(チェック例外)」を「非検査例外(非チェック例外)」として発生させることができる。
検査例外のある処理をラムダ式に入れ、その外側で検査例外を再発生させる
例えばこういうことだ。
void foo() throws IOException {
someStream.map(rethrowFunction(some->IOExceptionが発生する処理)).さらなる処理;
}
などとして、rethrowFunction()でくるむことにより、その内部にIOExceptionが発生する処理を入れ込むことができ、さらに外側にIOExceptionを伝達することができる。この場合はfoo()メソッドにIOException発生を宣言しなければならない。
そもそも検査例外を非検査例外として発生させる
こんなことも可能である。
void foo() {
someStream.map(uncheck(some->IOExceptionが発生する処理)).さらなる処理;
}
uncheck()でIOExceptionの発生する処理をくるんでしまうと、見かけはIOExceptionが発生しないようにできる。したがって、foo()はIOExceptionを宣言する必要はない。しかしこれでも、実際にはIOExceptionは発生する。Javaの構文に親しんでいると非常に奇妙なのだが、こう考えればよい、「コンパイラを騙して、字面上はIOExceptionが発生しないかのように見せかけているが、実際には発生する」ということだ。
LambdaExceptionUtilのダウンロードとimportの仕方
特にライブラリ形式にはなってはいない。
How can I throw CHECKED exceptions from inside Java 8 streams?の書き込みの中のLambdaExceptionUtil.javaをコピーペストして自身のLambdaExceptionUtilクラスを作成すればよい。
※jomoespe/LambdaExceptionUtil.javaにもあるが、これは若干バージョンが違う。rethrowFunction等の「throws E」が削除されている。
すべてがstaticメソッドになっているので、importとしてはstatic importをしておくのが簡単だ。例えばsampleパッケージに入れたとすると、
import static sample.LabmdaExceptionUtil.*;
とすればよい。
Stackoverflow版のLambdaExceptionUtilソース
こちらにStackOverflow版の全ソースをあげておく。最初の「import java.util.function.*;」は必須なので追加した。
import java.util.function.*;
public final class LambdaExceptionUtil {
@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
void accept(T t) throws E;
}
@FunctionalInterface
public interface BiConsumer_WithExceptions<T, U, E extends Exception> {
void accept(T t, U u) throws E;
}
@FunctionalInterface
public interface Function_WithExceptions<T, R, E extends Exception> {
R apply(T t) throws E;
}
@FunctionalInterface
public interface Supplier_WithExceptions<T, E extends Exception> {
T get() throws E;
}
@FunctionalInterface
public interface Runnable_WithExceptions<E extends Exception> {
void run() throws E;
}
/** .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name)))); or .forEach(rethrowConsumer(ClassNameUtil::println)); */
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); }
};
}
public static <T, U, E extends Exception> BiConsumer<T, U> rethrowBiConsumer(BiConsumer_WithExceptions<T, U, E> biConsumer) throws E {
return (t, u) -> {
try { biConsumer.accept(t, u); }
catch (Exception exception) { throwAsUnchecked(exception); }
};
}
/** .map(rethrowFunction(name -> Class.forName(name))) or .map(rethrowFunction(Class::forName)) */
public static <T, R, E extends Exception> Function<T, R> rethrowFunction(Function_WithExceptions<T, R, E> function) throws E {
return t -> {
try { return function.apply(t); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
};
}
/** rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))), */
public static <T, E extends Exception> Supplier<T> rethrowSupplier(Supplier_WithExceptions<T, E> function) throws E {
return () -> {
try { return function.get(); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
};
}
/** uncheck(() -> Class.forName("xxx")); */
public static void uncheck(Runnable_WithExceptions t)
{
try { t.run(); }
catch (Exception exception) { throwAsUnchecked(exception); }
}
/** uncheck(() -> Class.forName("xxx")); */
public static <R, E extends Exception> R uncheck(Supplier_WithExceptions<R, E> supplier)
{
try { return supplier.get(); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
}
/** uncheck(Class::forName, "xxx"); */
public static <T, R, E extends Exception> R uncheck(Function_WithExceptions<T, R, E> function, T t) {
try { return function.apply(t); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
}
@SuppressWarnings ("unchecked")
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E { throw (E)exception; }
}
検査例外の発生するfunctionとsupplierの例
例えば以下のようなコードがあるとする。
public void test() {
Optional<String>opt = Optional.empty();
String result = opt.map(s-> {
return functionMethod(s);
}).orElseGet(()-> {
return supplierMethod();
});
// もちろん、この場合は以下としてもよい
//String result = opt.map(this::functionMethod).orElseGet(this::supplierMethod);
}
private String functionMethod(String s) {
return s;
}
private String supplierMethod() {
return null;
}
ここでfunctionMethod, supplierMethodがIOExceptionを出す場合はどうするかと言えば、
import static sample.LabmdaExceptionUtil.*;
....
public void test() throws IOException { // 外側にIOExceptionを伝達する
Optional<String>opt = Optional.empty();
String result = opt.map(rethrowFunction(s-> { // rethrowFunctionでくるむ
return functionMethod(s);
})).orElseGet(rethrowSupplier(()-> { // rethrowSupplierでくるむ
return supplierMethod();
}));
// もちろんこうでも良い
String result = opt.map(rethrowFunction(this::functionMethod))
.orElseGet(rethrowSupplier(this::supplierMethod));
}
private String functionMethod(String s) throws IOException { // この例外が発生する
return s;
}
private String supplierMethod() throws IOException { // この例外が発生する
return null;
}
検査例外を非検査例外にしてしまう方法
検査例外を非検査例外として発生させる方法は以下だ。先の例とは少々形が異なる。
public void test() { // みかけはIOExceptionが発生しないが、実際には発生する。
Optional<String>opt = Optional.empty();
String result = opt.map(s->uncheck(t-> {
return functionMethod(t);
}, s)).orElse(uncheck(()->supplierMethod()));
// もちろんこうでも良い
//String result = opt.map(s->uncheck(this::functionMethod, s)).orElse(uncheck(this::supplierMethod));
}
private String functionMethod(String s) throws IOException { // この例外が発生する
return s;
}
private String supplierMethod() throws IOException { // この例外が発生する
return null;
}
改良バージョンの作成
オリジナルやその改良版は少々面倒と感じる。もう少し使いやすくしてみよう。これが目的を果たしてくれるのか、十分テストしてはいない。
基本的にはrethrow()でくるめば外側に例外が伝達され、uncheck()でくるめば(字面上は)伝達されない(が、例外は発生する)。
import java.util.function.*;
public final class LambdaExceptionUtil {
@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
void accept(T t) throws E;
}
@FunctionalInterface
public interface BiConsumer_WithExceptions<T, U, E extends Exception> {
void accept(T t, U u) throws E;
}
@FunctionalInterface
public interface Function_WithExceptions<T, R, E extends Exception> {
R apply(T t) throws E;
}
@FunctionalInterface
public interface Supplier_WithExceptions<T, E extends Exception> {
T get() throws E;
}
@FunctionalInterface
public interface Runnable_WithExceptions<E extends Exception> {
void run() throws E;
}
public static <T, E extends Exception> Consumer<T> rethrowC(Consumer_WithExceptions<T, E> consumer) throws E {
return uncheckC(consumer);
}
public static <T, E extends Exception> Consumer<T> uncheckC(Consumer_WithExceptions<T, E> consumer) {
return t -> {
try {
consumer.accept(t);
} catch (Exception exception) {
throwAsUnchecked(exception);
}
};
}
public static <T, U, E extends Exception> BiConsumer<T, U> rethrowB(BiConsumer_WithExceptions<T, U, E> biConsumer) throws E {
return uncheckB(biConsumer);
}
public static <T, U, E extends Exception> BiConsumer<T, U> uncheckB(BiConsumer_WithExceptions<T, U, E> biConsumer) {
return (t, u) -> {
try {
biConsumer.accept(t, u);
} catch (Exception exception) {
throwAsUnchecked(exception);
}
};
}
public static <T, R, E extends Exception> Function<T, R> rethrowF(Function_WithExceptions<T, R, E> function) throws E {
return uncheckF(function);
}
public static <T, R, E extends Exception> Function<T, R> uncheckF(Function_WithExceptions<T, R, E> function) {
return t -> {
try {
return function.apply(t);
} catch (Exception exception) {
throwAsUnchecked(exception);
return null;
}
};
}
public static <T, E extends Exception> Supplier<T> rethrowS(Supplier_WithExceptions<T, E> supplier) throws E {
return uncheckS(supplier);
}
public static <T, E extends Exception> Supplier<T> uncheckS(Supplier_WithExceptions<T, E> supplier) {
return () -> {
try {
return supplier.get();
} catch (Exception exception) {
throwAsUnchecked(exception);
return null;
}
};
}
public static <E extends Exception> Runnable rethrowR(Runnable_WithExceptions<E> runnable) throws E {
return uncheckR(runnable);
}
public static <E extends Exception> Runnable uncheckR(Runnable_WithExceptions<E> runnable) {
return () -> {
try {
runnable.run();
} catch (Exception exception) {
throwAsUnchecked(exception);
}
};
}
@SuppressWarnings("unchecked")
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E {
throw (E) exception;
}
}