Tratamento de erros de estilo funcional em Java

Além da abordagem clássica para lidar com erros usando exceções, também é possível distinguir uma abordagem funcional.

Em vez de lançar uma exceção imediatamente, você pode localizá-la e executar determinadas ações nela.

Por exemplo, no Scala, uma classe Try específica é usada para isso.

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

No mundo Java, usando a biblioteca Vavr, você também pode manipular erros em um estilo funcional.

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

No Java 8, a classe Opcional foi incluída para funcionar mais corretamente com tipos nulos. Ele permite envolver um objeto, que pode ser nulo, e em um estilo funcional, é seguro trabalhar com ele ainda mais.

Para trabalhar com diferentes tipos de exceções, você pode implementar uma classe semelhante ao Opcional, que também é segura para threads e não mutável.

 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; } } 

Além disso, para verificar se uma exceção foi lançada ou não, você pode escrever um visitante simples.

 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); } } 

No C ++ 23, está planejado adicionar uma classe semelhante à biblioteca padrão.

Essa classe pode constituir uma boa empresa opcional na biblioteca Java padrão. Porém, no momento, para trabalhar com exceções em um estilo funcional, você pode usar a biblioteca Vavr ou escrever suas próprias classes à semelhança de Expected.

O código fonte completo da classe pode ser visualizado no github: link .

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


All Articles