Gestion des erreurs de style fonctionnel en Java

En plus de l'approche classique pour gérer les erreurs à l'aide d'exceptions, une approche fonctionnelle peut également être distinguée.

Au lieu de lancer immédiatement une exception, vous pouvez la localiser, puis effectuer certaines actions dessus.

Par exemple, dans Scala, une classe Try spécifique est utilisée pour cela.

def inputStreamForURL(url: String): Try[Try[Try[InputStream]]] = parseURL(url).map { u => Try(u.openConnection()).map(conn => Try(conn.getInputStream)) } 

Dans le monde Java, en utilisant la bibliothèque Vavr, vous pouvez également gérer les erreurs dans un style fonctionnel.

 Try.of(() -> u.openConnection()).getOrElse(other); 

Dans Java 8, la classe Optional a été ajoutée pour fonctionner plus correctement avec les types null. Il vous permet d'envelopper un objet, qui peut être nul, et dans un style fonctionnel, il est plus sûr de travailler avec lui.

Pour travailler avec différents types d'exceptions, vous pouvez implémenter une classe similaire à Optional, qui est également thread-safe et non mutable.

 public final class Expected<T, E extends Throwable> { private final T value; private final E error; private Expected() { this.value = null; this.error = null; } private Expected(T value) { this.value = Objects.requireNonNull(value); this.error = null; } private Expected(E error) { this.error = Objects.requireNonNull(error); this.value = null; } public static <T, E extends Throwable> Expected<T, E> of(T value) { return new Expected<>(value); } public static <T, E extends Throwable> Expected<T, E> of(E error) { return new Expected<>(error); } public static <T, E extends Throwable> Expected<T, E> of(Supplier<T> supplier) { try { return new Expected<>(supplier.get()); } catch (Throwable e) { return new Expected<>((E) e); } } public boolean isValue() { return value != null; } public boolean isError() { return error != null; } public T value() { return value; } public E error() { return error; } } 

De plus, pour vérifier si une exception a été levée ou non, vous pouvez écrire un simple visiteur.

 Expected<Integer, SQLException> data = Expected.of(new SQLException()); matches(data, Expected::error, e -> out.println("get error: " + e), Expected::value, v -> out.println("get value: " + v) ); Expected<Integer, ArithmeticException> expression = Expected.of(() -> 4 / 0); matches(expression, Expected::error, e -> out.println("get error: " + e), Expected::value, v -> out.println("get value: " + v) ); 

 public static <T, E extends Throwable> void matches(Expected<T, E> value, Function<Expected<T, E>, E> firstFunction, Consumer<E> firstBranch, Function<Expected<T, E>, T> secondFunction, Consumer<T> secondBranch) { if (value.isError()) { E arg = firstFunction.apply(value); firstBranch.accept(arg); } else { T arg = secondFunction.apply(value); secondBranch.accept(arg); } } 

En C ++ 23, il est prévu d'ajouter une classe similaire à la bibliothèque standard.

Une telle classe pourrait constituer une bonne entreprise facultative dans la bibliothèque Java standard. Mais hélas, pour le moment, pour travailler avec des exceptions dans un style fonctionnel, vous pouvez utiliser la bibliothèque Vavr ou écrire vos propres classes à l'image d'Espected.

Le code source complet de la classe peut être consulté sur github: link .

Source: https://habr.com/ru/post/fr469329/


All Articles