Możesz spróbować tego:
void deleteEntity(Node node) throws SomeException { node.getChildren().forEach(UtilException.rethrowConsumer(this::deleteChild));
}
Klasa UtilException
pomocnik poniżej pozwala użyć dowolnego sprawdzone wyjątków w Javie strumieni. Zauważ, że powyższy strumień również zgłasza oryginalny zaznaczony wyjątek zgłoszony przez this::deleteChild
, a NIE niektóre zawijanie niezaznaczonego wyjątku.
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; }
}
wiele innych przykładów, w jaki sposób z niego korzystać (po statycznie importowania 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");
}
ale nie używaj go przed zrozumienia następujących zalet, wad i ograniczeń:
• Jeśli kod wywoławczy ma obsłużyć sprawdzony wyjątek, MUSISZ dodać go do klauzuli throws metody zawierającej strumień. Kompilator nie wymusi już dodania go, więc łatwiej go zapomnieć.
• Jeśli kod wywołujący już obsługuje sprawdzony wyjątek, kompilator BĘDZIE przypominał, aby dodać klauzulę rzutów do deklaracji metody , która zawiera strumień (jeśli nie, to powie: Wyjątek nigdy nie jest wrzucany treść odpowiadającej instrukcji try).
• W każdym przypadku nie będzie można otoczyć samego strumienia, aby przechwycić zaznaczony wyjątek WEWNĄTRZ metody zawierającej strumień (jeśli spróbujesz, kompilator powie: Wyjątek nigdy nie zostanie zgłoszony w treści odpowiednia instrukcja try).
• Jeśli wywołujesz metodę, która nigdy nie może rzucić wyjątku, który deklaruje, nie powinieneś zawierać klauzuli throws. Na przykład: nowy ciąg (byteArr, "UTF-8") zgłasza UnsupportedEncodingException, ale kodowanie UTF-8 jest gwarantowane przez specyfikację Java, aby zawsze była obecna. Tutaj deklaracja rzutów jest uciążliwa i mile widziane jest każde rozwiązanie, które ją uciszy przy minimalnym ustawieniu.
• Jeśli nienawidzisz sprawdzonych wyjątków i uważasz, że nigdy nie powinny być one dodawane do języka Java na początku (coraz więcej osób myśli w ten sposób, i NIE jestem jednym z nich), to po prostu nie dodawaj sprawdzony wyjątek do klauzuli throws metody zawierającej strumień. Sprawdzony wyjątek będzie zachowywał się tak jak wyjątek Nieprawidłowy.
• Jeśli implementujesz ścisły interfejs, w którym nie masz możliwości dodawania deklaracji rzutów, a mimo to wyrzucenie wyjątku jest całkowicie odpowiednie, , to zawijanie wyjątku tylko po to, aby uzyskać przywilej rzucania go, powoduje stacktrace z fałszywymi wyjątkami, które nie zawierają żadnych informacji o tym, co faktycznie było nie tak. Dobrym przykładem jest Runnable.run(), która nie wyrzuca żadnych sprawdzonych wyjątków. W takim przypadku możesz nie dodawać sprawdzanego wyjątku do klauzuli throws metody zawierającej strumień.
• W każdym razie, jeśli nie zdecydujesz się dodać (lub zapomnij dodać) sprawdzonej wyjątek rzutów klauzuli metody, która zawiera strumień, zdawać sobie sprawę z tych 2 konsekwencji rzuca Sprawdzone wyjątkami:
1) Kod wywołujący nie będzie mógł go przechwycić po nazwie (jeśli spróbujesz, kompilator powie: Wyjątek nigdy nie zostanie zgłoszony w treści odpowiadającej oświadczeniu ). Będzie bąbelkować i prawdopodobnie zostanie przechwycony w głównej pętli programu przez "wyjątek catch" lub "catch Throwable", który i tak może być potrzebny.
2) Narusza on zasadę najmniejszego zaskoczenia: nie wystarczy już uchwycić wyjątku RuntimeException, aby móc zagwarantować możliwość złapania wszystkich możliwych wyjątków od . Z tego powodu uważam, że nie powinno się tego robić w kodzie źródłowym, ale tylko w kodzie biznesowym, który całkowicie kontrolujesz.
Podsumowując: uważam, że ograniczenia tutaj nie są poważne, a klasa UtilException
może być używana bez obaw. Jednak to zależy od Ciebie!
Na marginesie to nie jest wyrażenie lambda - jest to odwołanie do metody. Byłby to wyrażenie lambda, jeśli użyjesz 'forEach (x -> deleteChild (x))'. To by jednak zawiodło z tego samego powodu. –