このUtilException
ヘルパー クラスを使用すると、次のように Java ストリームでチェック済み例外を使用できます。
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(rethrowFunction(Class::forName))
.collect(Collectors.toList());
Class::forName
チェックClassNotFoundException
されていることに注意してください。ストリーム自体も をスローしますがClassNotFoundException
、ラッピングされていない未チェックの例外はスローしません。
public final class UtilException {
@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; }
}
それを使用する方法に関する他の多くの例(静的にインポートした後UtilException
):
@Test
public void test_Consumer_with_checked_exceptions() throws IllegalAccessException {
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.forEach(rethrowConsumer(className -> System.out.println(Class.forName(className))));
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.forEach(rethrowConsumer(System.out::println));
}
@Test
public void test_Function_with_checked_exceptions() throws ClassNotFoundException {
List<Class> classes1
= Stream.of("Object", "Integer", "String")
.map(rethrowFunction(className -> Class.forName("java.lang." + className)))
.collect(Collectors.toList());
List<Class> classes2
= Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(rethrowFunction(Class::forName))
.collect(Collectors.toList());
}
@Test
public void test_Supplier_with_checked_exceptions() throws ClassNotFoundException {
Collector.of(
rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))),
StringJoiner::add, StringJoiner::merge, StringJoiner::toString);
}
@Test
public void test_uncheck_exception_thrown_by_method() {
Class clazz1 = uncheck(() -> Class.forName("java.lang.String"));
Class clazz2 = uncheck(Class::forName, "java.lang.String");
}
@Test (expected = ClassNotFoundException.class)
public void test_if_correct_exception_is_still_thrown_by_method() {
Class clazz3 = uncheck(Class::forName, "INVALID");
}
ただし、次の利点、欠点、および制限を理解する前に使用しないでください。
• 呼び出しコードがチェック済み例外を処理する場合、ストリームを含むメソッドの throws 節にそれを追加する必要があります。コンパイラはそれを追加することを強制しなくなるので、忘れやすくなります。
• 呼び出し元のコードが既にチェック済みの例外を処理している場合、コンパイラは、ストリームを含むメソッド宣言に throws 句を追加するよう通知します (そうしない場合、対応する try ステートメントの本体で例外がスローされることはありません)。 )。
• いずれの場合でも、ストリームを含むメソッド内でチェック済みの例外をキャッチするために、ストリーム自体を囲むことはできません (試してみると、コンパイラは次のように言います:対応する try ステートメントの本体で例外がスローされることはありません)。
• 宣言した例外を文字どおりスローできないメソッドを呼び出す場合は、throws 句を含めないでください。例: new String(byteArr, "UTF-8") は UnsupportedEncodingException をスローしますが、UTF-8 は Java 仕様によって常に存在することが保証されています。ここで、 throws 宣言は厄介であり、最小限のボイラープレートでそれを沈黙させる解決策は大歓迎です。
• チェック例外が嫌いで、最初から Java 言語に追加するべきではないと感じている場合 (このように考える人が増えていますが、私もその一人ではありません)、チェック例外を Java 言語に追加しないでください。ストリームを含むメソッドの throws 句。チェックされた例外は、チェックされていない例外と同じように動作します。
• throws 宣言を追加するオプションがなく、例外をスローすることが完全に適切な厳密なインターフェイスを実装している場合、例外をスローする特権を得るために例外をラップすると、偽の例外を伴うスタックトレースが発生します。実際に何がうまくいかなかったのかについての情報を提供しません。良い例は Runnable.run() で、これはチェック例外をスローしません。この場合、チェック済み例外を、ストリームを含むメソッドの throws 節に追加しないことを決定できます。
• いずれにしても、ストリームを含むメソッドの throws 節にチェック例外を追加しない (または追加するのを忘れる) 場合は、CHECKED 例外をスローすることによる次の 2 つの結果に注意してください。
1) 呼び出し元コードは名前でそれをキャッチできません (試してみると、コンパイラーは「対応する try ステートメントの本体で例外がスローされることはありません」と言うでしょう)。それはバブリングし、おそらく何らかの「catch Exception」または「catch Throwable」によってメイン プログラム ループでキャッチされます。
2) これは最小の驚きの原則に違反しています。可能性のあるすべての例外を確実にキャッチするには、RuntimeException をキャッチするだけでは十分ではありません。このため、これはフレームワーク コードではなく、完全に制御できるビジネス コードでのみ行うべきだと思います。
結論として、ここでの制限は深刻ではなく、UtilException
クラスは恐れることなく使用できると思います。しかし、それはあなた次第です!