Lớp trình UtilException
trợ giúp này cho phép bạn sử dụng bất kỳ ngoại lệ được kiểm tra nào trong các luồng Java, như thế này:
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(rethrowFunction(Class::forName))
.collect(Collectors.toList());
Lưu ý Class::forName
ném ClassNotFoundException
, được kiểm tra . Luồng cũng tự ném ClassNotFoundException
và KHÔNG một số ngoại lệ không được kiểm soát.
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; }
}
Nhiều ví dụ khác về cách sử dụng nó (sau khi nhập tĩnh 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");
}
Nhưng đừng sử dụng nó trước khi hiểu những ưu điểm, nhược điểm và hạn chế sau:
• Nếu mã gọi là để xử lý ngoại lệ được kiểm tra, bạn PHẢI thêm nó vào mệnh đề ném của phương thức có chứa luồng. Trình biên dịch sẽ không buộc bạn thêm nó nữa, vì vậy việc quên nó sẽ dễ dàng hơn.
• Nếu mã gọi đã xử lý ngoại lệ được kiểm tra, trình biên dịch SILL nhắc nhở bạn thêm mệnh đề ném vào khai báo phương thức có chứa luồng (nếu bạn không nói: Ngoại lệ sẽ không bao giờ được ném trong phần thân của câu lệnh thử tương ứng ).
• Trong mọi trường hợp, bạn sẽ không thể bao quanh chính luồng đó để bắt ngoại lệ đã kiểm tra bên trong phương thức có chứa luồng (nếu bạn thử, trình biên dịch sẽ nói: Ngoại lệ không bao giờ được đưa vào phần thân của câu lệnh thử tương ứng).
• Nếu bạn đang gọi một phương thức mà theo nghĩa đen không bao giờ có thể ném ngoại lệ mà nó tuyên bố, thì bạn không nên bao gồm mệnh đề ném. Ví dụ: Chuỗi mới (byteArr, "UTF-8") ném UnsupportedEncodingException, nhưng UTF-8 được đảm bảo bởi đặc tả Java luôn luôn có mặt. Ở đây, tuyên bố ném là một phiền toái và bất kỳ giải pháp nào để làm im lặng nó với bản tóm tắt tối thiểu đều được hoan nghênh.
• Nếu bạn ghét các ngoại lệ được kiểm tra và cảm thấy không bao giờ nên thêm chúng vào ngôn ngữ Java (ngày càng nhiều người nghĩ theo cách này và tôi KHÔNG phải là một trong số họ), thì đừng thêm ngoại lệ được kiểm tra vào ném mệnh đề của phương thức chứa luồng. Sau đó, ngoại lệ được kiểm tra sẽ hoạt động giống như một ngoại lệ không được kiểm tra.
• Nếu bạn đang triển khai một giao diện nghiêm ngặt, nơi bạn không có tùy chọn để thêm một tuyên bố ném, nhưng việc ném một ngoại lệ là hoàn toàn phù hợp, thì gói một ngoại lệ chỉ để có được đặc quyền ném nó dẫn đến một ngăn xếp ngoại lệ trong đó đóng góp không có thông tin về những gì thực sự đã đi sai. Một ví dụ điển hình là Runnable.run (), không đưa ra bất kỳ ngoại lệ nào được kiểm tra. Trong trường hợp này, bạn có thể quyết định không thêm ngoại lệ được kiểm tra vào mệnh đề ném của phương thức có chứa luồng.
• Trong mọi trường hợp, nếu bạn quyết định KHÔNG thêm (hoặc quên thêm) ngoại lệ được kiểm tra vào mệnh đề ném của phương thức có chứa luồng, hãy lưu ý đến 2 hậu quả của việc ném ngoại lệ CHECKED:
1) Mã cuộc gọi sẽ không thể bắt được nó theo tên (nếu bạn thử, trình biên dịch sẽ nói: Ngoại lệ không bao giờ được đưa vào phần thân của câu lệnh thử tương ứng). Nó sẽ sủi bọt và có thể bị bắt trong vòng lặp chương trình chính bởi một số "bắt Ngoại lệ" hoặc "bắt Ném được", đây có thể là những gì bạn muốn.
2) Nó vi phạm nguyên tắc ít gây ngạc nhiên nhất: sẽ không còn đủ để bắt RuntimeException để có thể đảm bảo nắm bắt tất cả các ngoại lệ có thể. Vì lý do này, tôi tin rằng điều này không nên được thực hiện trong mã khung, mà chỉ trong mã doanh nghiệp mà bạn hoàn toàn kiểm soát.
Tóm lại: Tôi tin rằng những hạn chế ở đây là không nghiêm trọng, và UtilException
lớp học có thể được sử dụng mà không sợ hãi. Tuy nhiên, tùy bạn!