Mit dieser UtilException
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 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; }
}
Viele andere Beispiele zur Verwendung (nach dem statischen Import 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");
}
Verwenden Sie es jedoch nicht, bevor Sie die folgenden Vor- und Nachteile sowie Einschränkungen verstanden haben :
• Wenn der aufrufende Code die aktivierte Ausnahme behandeln soll, MÜSSEN Sie ihn der throw-Klausel der Methode hinzufügen, die den Stream enthält. Der Compiler zwingt Sie nicht mehr, es hinzuzufügen, daher ist es einfacher, es zu vergessen.
• Wenn der aufrufende Code die aktivierte Ausnahme bereits verarbeitet, erinnert Sie der Compiler daran, die throw-Klausel zur Methodendeklaration hinzuzufügen, die den Stream enthält (wenn Sie dies nicht tun, wird Folgendes angegeben: Die Ausnahme wird niemals im Hauptteil der entsprechenden try-Anweisung ausgelöst ).
• In jedem Fall können Sie den Stream selbst nicht umgeben, um die aktivierte Ausnahme INNERHALB der Methode, die den Stream enthält, abzufangen (wenn Sie es versuchen, sagt der Compiler: Ausnahme wird niemals in den Text der entsprechenden try-Anweisung ausgelöst).
• Wenn Sie eine Methode aufrufen, die buchstäblich nie die von ihr deklarierte Ausnahme auslösen kann, sollten Sie die throw-Klausel nicht einschließen. 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.
• Wenn Sie geprüfte Ausnahmen hassen und der Meinung sind, dass sie niemals der Java-Sprache hinzugefügt werden sollten (eine wachsende Anzahl von Menschen denkt so, und ich bin NICHT einer von ihnen), fügen Sie die geprüfte Ausnahme einfach nicht zu der hinzu löst eine Klausel der Methode aus, die den Stream enthält. Die aktivierte Ausnahme verhält sich dann wie eine nicht aktivierte Ausnahme.
• Wenn Sie eine strikte Schnittstelle implementieren, in der Sie nicht die Möglichkeit haben, eine Throws-Deklaration hinzuzufügen, und dennoch das Auslösen einer Ausnahme völlig angemessen ist, führt das Umschließen einer Ausnahme, um das Privileg zu erhalten, sie auszulösen, zu einer Stapelverfolgung mit falschen Ausnahmen die keine Informationen darüber beitragen, was tatsächlich schief gelaufen ist. Ein gutes Beispiel ist Runnable.run (), das keine überprüften Ausnahmen auslöst. In diesem Fall können Sie entscheiden, die aktivierte Ausnahme nicht zur throw-Klausel der Methode hinzuzufügen, die den Stream enthält.
• Wenn Sie die aktivierte Ausnahme NICHT zur Throws-Klausel der Methode, die den Stream enthält, hinzufügen (oder vergessen, sie hinzuzufügen), sollten Sie sich in jedem Fall dieser beiden Konsequenzen bewusst sein, wenn Sie CHECKED-Ausnahmen auslösen:
1) Der aufrufende Code kann ihn nicht beim Namen abfangen (wenn Sie es versuchen, sagt der Compiler: Eine Ausnahme wird niemals in den Text der entsprechenden try-Anweisung geworfen). Es sprudelt und wird wahrscheinlich in der Hauptprogrammschleife von einer "catch Exception" oder "catch Throwable" abgefangen, was Sie vielleicht sowieso wollen.
2) Es verstößt gegen das Prinzip der geringsten Überraschung: Es reicht nicht mehr aus, RuntimeException abzufangen, um das Abfangen aller möglichen Ausnahmen garantieren zu können. Aus diesem Grund glaube ich, dass dies nicht im Framework-Code erfolgen sollte, sondern nur im Business-Code, den Sie vollständig kontrollieren.
Fazit: Ich glaube, die Einschränkungen hier sind nicht ernst und die UtilException
Klasse kann ohne Angst benutzt werden. Es liegt jedoch an Ihnen!