Cette UtilException
classe d'assistance vous permet d'utiliser toutes les exceptions vérifiées dans les flux Java, comme ceci:
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(rethrowFunction(Class::forName))
.collect(Collectors.toList());
Remarque Class::forName
jette ClassNotFoundException
, qui est vérifié . Le flux lui-même lève également ClassNotFoundException
, et PAS une exception non cochée d'encapsulation.
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; }
}
De nombreux autres exemples d'utilisation (après importation statique 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");
}
Mais ne l'utilisez pas avant de comprendre les avantages, inconvénients et limitations suivants :
• Si le code d'appel doit gérer l'exception vérifiée, vous DEVEZ l'ajouter à la clause throws de la méthode qui contient le flux. Le compilateur ne vous obligera plus à l'ajouter, il est donc plus facile de l'oublier.
• Si le code appelant gère déjà l'exception vérifiée, le compilateur vous rappellera d'ajouter la clause throws à la déclaration de méthode qui contient le flux (si vous ne le faites pas, il dira: l'exception n'est jamais levée dans le corps de l'instruction try correspondante ).
• Dans tous les cas, vous ne pourrez pas entourer le flux lui-même pour attraper l'exception vérifiée À L'INTÉRIEUR de la méthode qui contient le flux (si vous essayez, le compilateur dira: l'exception n'est jamais levée dans le corps de l'instruction try correspondante).
• Si vous appelez une méthode qui ne peut littéralement jamais lever l'exception qu'elle déclare, vous ne devez pas inclure la clause throws. Par exemple: new String (byteArr, "UTF-8") lève une exception UnsupportedEncodingException, mais UTF-8 est garanti par la spécification Java pour être toujours présent. Ici, la déclaration des lancers est une nuisance et toute solution pour la faire taire avec un passe-partout minimal est la bienvenue.
• Si vous détestez les exceptions vérifiées et pensez qu'elles ne devraient jamais être ajoutées au langage Java pour commencer (un nombre croissant de personnes pensent de cette façon, et je ne suis PAS l'un d'entre eux), alors n'ajoutez simplement pas l'exception vérifiée au throws clause de la méthode qui contient le flux. L'exception vérifiée se comportera alors comme une exception non vérifiée.
• Si vous implémentez une interface stricte dans laquelle vous n'avez pas la possibilité d'ajouter une déclaration throws, mais que la levée d'une exception est tout à fait appropriée, alors encapsuler une exception juste pour obtenir le privilège de la lancer entraîne un stacktrace avec des exceptions fausses qui ne fournissent aucune information sur ce qui a réellement mal tourné. Un bon exemple est Runnable.run (), qui ne lève aucune exception vérifiée. Dans ce cas, vous pouvez décider de ne pas ajouter l'exception vérifiée à la clause throws de la méthode qui contient le flux.
• Dans tous les cas, si vous décidez de NE PAS ajouter (ou oubliez d'ajouter) l'exception vérifiée à la clause throws de la méthode qui contient le flux, soyez conscient de ces 2 conséquences de lever des exceptions CHECKED:
1) Le code appelant ne pourra pas l'attraper par son nom (si vous essayez, le compilateur dira: l'exception n'est jamais levée dans le corps de l'instruction try correspondante). Il bouillonnera et sera probablement attrapé dans la boucle principale du programme par des "catch Exception" ou "catch Throwable", ce qui peut être ce que vous voulez de toute façon.
2) Cela viole le principe de moindre surprise: il ne suffira plus d'attraper RuntimeException pour pouvoir garantir la capture de toutes les exceptions possibles. Pour cette raison, je pense que cela ne devrait pas être fait dans le code-cadre, mais uniquement dans le code métier que vous contrôlez complètement.
En conclusion: je pense que les limites ici ne sont pas sérieuses et que la UtilException
classe peut être utilisée sans crainte. Cependant, c'est à vous!