Mit dieser LambdaExceptionUtil
Hilfsklasse können Sie alle überprüften Ausnahmen in Java-Streams wie folgt verwenden:
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(rethrowFunction(Class::forName))
.collect(Collectors.toList());
Note Class::forName
wirft ClassNotFoundException
, was überprüft wird . Der Stream selbst löst ebenfalls aus ClassNotFoundException
und NICHT eine nicht aktivierte Ausnahme beim Umschließen.
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; }
}
Viele andere Beispiele zur Verwendung (nach dem statischen Import 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");
}
ANMERKUNG 1: Die rethrow
Methoden der LambdaExceptionUtil
obigen Klasse können ohne Angst angewendet werden und sind in jeder Situation in Ordnung . Ein großes Dankeschön an den Benutzer @PaoloC, der zur Lösung des letzten Problems beigetragen hat: Jetzt werden Sie vom Compiler aufgefordert, Throw-Klauseln hinzuzufügen, und alles ist so, als könnten Sie geprüfte Ausnahmen nativ in Java 8-Streams auslösen.
ANMERKUNG 2: Die uncheck
Methoden derLambdaExceptionUtil
obigen Klasse sind Bonusmethoden und können sicher aus der Klasse entfernt werden, wenn Sie sie nicht verwenden möchten. Wenn Sie sie verwendet haben, tun Sie dies mit Vorsicht und nicht bevor Sie die folgenden Anwendungsfälle, Vor- / Nachteile und Einschränkungen verstanden haben:
• Sie können die uncheck
Methoden verwenden, wenn Sie eine Methode aufrufen, die buchstäblich niemals die von ihr deklarierte Ausnahme auslösen kann. Beispiel: new String (byteArr, "UTF-8") löst UnsupportedEncodingException aus, aber UTF-8 wird durch die Java-Spezifikation garantiert, dass es immer vorhanden ist. Hier ist die Wurferklärung ein Ärgernis und jede Lösung, um sie mit minimalem Boilerplate zum Schweigen zu bringen, ist willkommen:String text = uncheck(() -> new String(byteArr, "UTF-8"));
• Sie können die uncheck
Methoden verwenden, wenn Sie eine strikte Schnittstelle implementieren, bei der Sie nicht die Möglichkeit haben, eine Throws-Deklaration hinzuzufügen, und dennoch das Auslösen einer Ausnahme völlig angemessen ist. Das Umschließen einer Ausnahme, nur um das Privileg zu erhalten, sie auszulösen, führt zu einer Stapelverfolgung mit falschen Ausnahmen, die keine Informationen darüber liefern, was tatsächlich schief gelaufen ist. Ein gutes Beispiel ist Runnable.run (), das keine überprüften Ausnahmen auslöst.
• Wenn Sie sich für die Verwendung der uncheck
Methoden entscheiden, beachten Sie in jedem Fall die beiden Konsequenzen des Auslösens von CHECKED-Ausnahmen ohne Throws-Klausel: 1) Der aufrufende Code kann ihn nicht beim Namen abfangen (wenn Sie es versuchen, wird der Der Compiler wird sagen: Eine Ausnahme wird niemals in den Text der entsprechenden try-Anweisung geworfen. Es sprudelt und wird wahrscheinlich von einer "catch Exception" oder "catch Throwable" in der Hauptprogrammschleife abgefangen, was Sie vielleicht sowieso wollen. 2) Es verstößt gegen das Prinzip der geringsten Überraschung: Es reicht nicht mehr aus, um zu fangen RuntimeException
, um garantieren zu können, dass alle möglichen Ausnahmen gefangen werden. Aus diesem Grund glaube ich, dass dies nicht im Framework-Code erfolgen sollte, sondern nur im Business-Code, den Sie vollständig kontrollieren.