LambdaExceptionUtil
Kelas helper ini memungkinkan Anda menggunakan pengecualian yang dicentang di aliran Java, seperti ini:
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(rethrowFunction(Class::forName))
.collect(Collectors.toList());
Catatan Class::forName
lemparan ClassNotFoundException
, yang diperiksa . Aliran itu sendiri juga melempar ClassNotFoundException
, dan BUKAN beberapa pembungkus pengecualian terkendali.
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; }
}
Banyak contoh lain tentang cara menggunakannya (setelah mengimpor secara statis LambdaExceptionUtil
):
@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");
}
CATATAN 1: The rethrow
metode dari LambdaExceptionUtil
kelas di atas dapat digunakan tanpa rasa takut, dan OK untuk digunakan dalam situasi apa pun . Terima kasih banyak kepada pengguna @PaoloC yang membantu memecahkan masalah terakhir: Sekarang kompiler akan meminta Anda untuk menambahkan klausa melempar dan semuanya seolah-olah Anda bisa melempar pengecualian yang diperiksa secara native di stream Java 8.
CATATAN 2: The uncheck
metode dari LambdaExceptionUtil
kelas atas adalah metode bonus, dan dapat dengan aman menghapus mereka dari kelas jika Anda tidak ingin menggunakannya. Jika Anda menggunakannya, lakukan dengan hati-hati, dan jangan sebelum memahami kasus penggunaan berikut, kelebihan / kekurangan dan keterbatasan:
• Anda dapat menggunakan uncheck
metode jika Anda memanggil metode yang benar-benar tidak pernah dapat membuang pengecualian yang dinyatakannya. Sebagai contoh: String baru (byteArr, "UTF-8") melempar UnsupportedEncodingException, tetapi UTF-8 dijamin oleh spesifikasi Java untuk selalu hadir. Di sini, deklarasi melempar adalah gangguan dan solusi apa pun untuk membungkamnya dengan platplate minimal disambut:String text = uncheck(() -> new String(byteArr, "UTF-8"));
• Anda dapat menggunakan uncheck
metode jika Anda menerapkan antarmuka yang ketat di mana Anda tidak memiliki opsi untuk menambahkan deklarasi lemparan, namun melemparkan pengecualian sepenuhnya sesuai. Membungkus pengecualian hanya untuk mendapatkan hak istimewa dengan melemparkannya menghasilkan stacktrace dengan pengecualian palsu yang tidak berkontribusi informasi tentang apa yang sebenarnya salah. Contoh yang baik adalah Runnable.run (), yang tidak membuang pengecualian yang dicentang.
• Dalam kasus apa pun, jika Anda memutuskan untuk menggunakan uncheck
metode ini, waspadalah terhadap 2 konsekuensi membuang pengecualian yang DIPERIKSA tanpa klausa pelemparan: 1) Kode panggilan tidak akan dapat menangkapnya dengan nama (jika Anda mencoba, kompiler akan mengatakan: Pengecualian tidak pernah dilemparkan ke badan pernyataan percobaan yang sesuai). Ini akan menggelembung dan mungkin terperangkap dalam loop program utama oleh "catch Exception" atau "catch Throwable", yang mungkin merupakan apa yang Anda inginkan. 2) Ini melanggar prinsip paling tidak mengejutkan: itu tidak akan lagi cukup untuk menangkap RuntimeException
untuk dapat menjamin menangkap semua pengecualian yang mungkin. Untuk alasan ini, saya percaya ini tidak boleh dilakukan dalam kode kerangka kerja, tetapi hanya dalam kode bisnis yang sepenuhnya Anda kontrol.