UtilException
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 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; }
}
Banyak contoh lain tentang cara menggunakannya (setelah mengimpor secara statis 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");
}
Tetapi jangan menggunakannya sebelum memahami kelebihan, kekurangan, dan batasan berikut :
• Jika kode panggilan adalah untuk menangani pengecualian yang diperiksa, Anda HARUS menambahkannya ke klausa pelemparan dari metode yang berisi aliran. Kompiler tidak akan memaksa Anda untuk menambahkannya lagi, jadi lebih mudah untuk melupakannya.
• Jika kode panggilan sudah menangani pengecualian yang dicentang, kompiler AKAN mengingatkan Anda untuk menambahkan klausa pelemparan ke deklarasi metode yang berisi aliran (jika Anda tidak melakukannya akan mengatakan: Pengecualian tidak pernah dilemparkan ke badan pernyataan percobaan yang sesuai ).
• Dalam kasus apa pun, Anda tidak akan dapat mengelilingi aliran itu sendiri untuk menangkap pengecualian yang dicentang DI DALAM metode yang berisi aliran (jika Anda mencoba, kompiler akan mengatakan: Pengecualian tidak pernah dilemparkan ke badan pernyataan percobaan yang sesuai).
• Jika Anda memanggil metode yang benar-benar tidak pernah dapat membuang pengecualian yang dinyatakannya, maka Anda tidak boleh memasukkan klausa pelemparan. 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 boilerplate minimal disambut.
• Jika Anda membenci pengecualian yang dicentang dan merasa mereka tidak boleh ditambahkan ke bahasa Java untuk memulai (semakin banyak orang berpikir seperti ini, dan saya BUKAN salah satu dari mereka), maka jangan tambahkan pengecualian yang dicentang ke melempar klausa metode yang berisi aliran. Pengecualian yang dicentang akan, kemudian, berperilaku seperti pengecualian yang tidak dicentang.
• Jika Anda menerapkan antarmuka yang ketat di mana Anda tidak memiliki opsi untuk menambahkan deklarasi lemparan, namun melempar pengecualian sepenuhnya tepat, kemudian membungkus pengecualian hanya untuk mendapatkan hak istimewa 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 hal ini, Anda dapat memutuskan untuk tidak menambahkan pengecualian yang dicentang ke klausa melempar dari metode yang berisi aliran.
• Dalam hal apa pun, jika Anda memutuskan untuk TIDAK menambahkan (atau lupa menambahkan) pengecualian yang dicentang untuk klausa melempar dari metode yang berisi aliran, perhatikan 2 konsekuensi dari melemparkan pengecualian yang DIPERIKSA:
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 ditangkap 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 Anda kontrol penuh.
Kesimpulannya: Saya percaya batasan di sini tidak serius, dan UtilException
kelas dapat digunakan tanpa rasa takut. Namun, itu terserah Anda!