Ir para o conteúdo principal
Background Image

Java Stream: Como tratar exceções em paradigma funcional

··4 minutos·
Rafael Issao
Autor
Rafael Issao
Apaixonado por tecnologia, programação e inovação. Adoro compartilhar conhecimento e aprender coisas novas todos os dias.
Tabela de conteúdos

A partir do Java 8 surgiu a API do stream, facilitando ainda mais a escrita de código com paradigma funcional, tornando possível um código mais declarativo e menos imperativo.

Mas um problema comum de design que surge quando usamos o paradigma funcional é:

Como tratar as exceções?

Vamos ver como podemos tratar as exceções seguindo a ideia da programação funcional!

O problema
#

Veja o código abaixo:

    public static void main(String[] args) {
        final List<String> numbers = List.of("1", "2", "-32", "5", "100");

        // Imprime todos os números pares da lista
        numbers.stream()
                .map(Integer::parseInt)
                .filter(number -> number % 2 == 0)
                .forEach(System.out::println);
    }

Criamos uma stream a partir da lista de números representado por string e imprimimos todos os números que são pares.

O resultado impresso é

Mas o que acontece quando a lista possui uma string que não é um número?

    public static void main(String[] args) {
        // Temos um "oi" na lista
        final List<String> numbers = List.of("1", "2", "-32", "oi", "5", "100");

        // Imprime todos os números pares da lista
        numbers.stream()
                .map(Integer::parseInt) // LANÇA EXCEÇÃO!
                .filter(number -> number % 2 == 0)
                .forEach(System.out::println);
    }

Ocorre um NumberFormatException e resto dos elementos não são processados!

Para resolver este problema, vamos usar a idea do Try do Scala!

A ideia do Try
#

O Try é uma função que devolve duas possibilidades. Um Sucesso com o valor computado ou uma Falha com a exceção lançada pela computação:

No nosso exemplo, seria a função Integer::parseInt. Se passarmos, por exemplo a string "-32", o Try devolveria Success contendo o valor -32:

Se passarmos a string “oi”, o Try devolveria Failure contendo o NumberFormatException:

E assim começamos a tratar a exceção também como dado que faz parte do fluxo do stream.

Com este design não paramos o fluxo de dados da lista, mesmo que esta lista tenha dados inválidos para as funções compostas na stream.

Implementação utilizando VAVR
#

O VAVR tem uma implementação de Try para o java!

Vamos utilizar esta biblioteca e ver como fica o código.

Primeiro vamos criar uma função que vai tentar transformar um texto em um inteiro:

    public static void main(String[] args) {
        final List<String> numbers = List.of("1", "2", "-32", "oi", "5", "100");

        numbers.stream().map(TryTo::parseInt).filter(number -> number % 2 == 0).forEach(System.out::println);
    }

    static class TryTo {
        // Vamos implementar a funcao Try aqui!
        static int parseInt(String text) {
            return Integer.parseInt(text);
        }
    }
}

Agora vamos usar o Try do VAVR:

    public static void main(String[] args) {
        final List<String> numbers = List.of("1", "2", "-32", "oi", "5", "100");

        numbers.stream().map(TryTo::parseInt).filter(number -> number % 2 == 0).forEach(System.out::println);
    }

    static class TryTo {
        private static int parseInt(String text) {
            // Utilizando o Try do VAVR para transformar em inteiro
            return Try.of(() -> Integer.parseInt(text))
                    // Fazer algo quando ocorre alguma excecao
                    .onFailure((e) -> System.out.println(e.getMessage()))
                    // Devolver o valor computado ou um valor padrao quando ocorre excecao.
                    // Aqui estamos devolvendo -1 que nao influencia nas proximas funcoes.
                    .getOrElse(-1);
        }
    }
}

Com este novo design, teremos as seguintes saídas:

Agora temos os números pares impressos e conseguimos tratar a exceção.

Existem muitas outras formas de tratar exceções com este padrão do Try:

https://docs.vavr.io/#_try

Vale a pena dar uma conferida!

Conclusão
#

Muitas vezes ficamos em uma linguagem e ficamos preso na familiaridade do design que esta linguagem fornece.

No caso do Stream do Java é a mesma situação. Acostumamos com design imperativo e orientada à objetos e quando temos a possibilidade de utilizar um paradigma novo, tentamos usar conhecimento familiares em vez de aprender o design novo que o paradigma fornece.

Para ver uma implementação sendo feita do Try, recomendo este vídeo:

Para aprender a programar melhor utilizando o paradigma funcional recomendo:

https://elm-lang.org/ - Uma linguagem encantadora para aplicações web confiáveis.

https://www.haskell.org/ - Uma linguagem de programação avançada, puramente funcional.

Vai tirar você da zona de conforto e ajudá-lo a pensar de uma nova forma. Recomendo!

Se você tiver dúvidas, sugestões ou até correções, sinta-se à vontade para comentar ou falar diretamente comigo!

🥒🥒 Até o próximo artigo! 🥒🥒