Abordagem funcional vs orientada a objetos em Java: o que escolher para o desenvolvedor moderno?
\n\nO mundo Java é um campo de batalha de dois gigantes. De um lado, a clássica programação orientada a objetos (POO), na qual uma geração inteira de desenvolvedores cresceu. Do outro, a abordagem funcional ganhando força, que invadiu o ecossistema com o lançamento do Java 8 e ainda gera debates: "Será que precisamos disso?".
\n\nSe você programa em Java, enfrenta essa escolha diariamente. Vale a pena criar uma classe inteira com métodos para uma simples filtragem? Ou é melhor encadear lambdas e esquecer? Neste artigo, não vamos tomar partido. Analisaremos ambas as abordagens com exemplos reais, compararemos métricas-chave e ajudaremos você a entender quando usar cada ferramenta do arsenal.
\n\nBreve histórico: como chegamos até aqui
\n\nO Java foi originalmente concebido como uma linguagem puramente orientada a objetos. Tudo é objeto. Até uma string é um objeto da classe String. Classes, herança, polimorfismo — isso era a religião. Mas com o tempo, os desenvolvedores perceberam: a POO modela bem o mundo real (carros, funcionários, pedidos), mas lida mal com o processamento de fluxos de dados.
Foi então que entraram em cena as expressões lambda e a Stream API. O Java não se tornou uma linguagem funcional (como Haskell ou Scala), mas absorveu o melhor desse paradigma. Agora temos a escolha: escrever um loop for dentro de um método de classe ou usar filter().map().collect().
Abordagem orientada a objetos: o clássico do gênero
\n\nA POO em Java é construída sobre quatro pilares: encapsulamento, herança, polimorfismo e abstração. Quando você trabalha com lógica de negócios que envolve entidades (Usuário, Pedido, Produto), a POO é a escolha natural.
\n\nAqui está um exemplo de tarefa clássica: calcular a soma de todos os pedidos de um cliente feitos em 2024.
\n\nSolução orientada a objetos:
\n\nimport java.util.List;\n\npublic class OrderService {\n\n public double calculateTotal2024(List<Order> orders) {\n double total = 0.0;\n for (Order order : orders) {\n if (order.getYear() == 2024) {\n total += order.getAmount();\n }\n }\n return total;\n }\n}\n\nclass Order {\n private int year;\n private double amount;\n\n public Order(int year, double amount) {\n this.year = year;\n this.amount = amount;\n }\n\n public int getYear() { return year; }\n public double getAmount() { return amount; }\n}\n\nTudo transparente, legível, fácil de depurar. Qualquer iniciante entenderá o que está acontecendo. Mas há um detalhe: o código é imperativo. Dizemos como fazer (crie uma variável, percorra a lista, verifique a condição, some).
\n\nAbordagem funcional: escrevendo de forma declarativa
\n\nA programação funcional em Java é sobre o que queremos obter, e não como. Usamos funções de alta ordem, imutabilidade e ausência de efeitos colaterais.
\n\nA mesma tarefa, mas no estilo funcional:
\n\nSolução funcional:
\n\nimport java.util.List;\n\npublic class OrderServiceFunctional {\n\n public double calculateTotal2024(List<Order> orders) {\n return orders.stream()\n .filter(order -> order.getYear() == 2024)\n .mapToDouble(Order::getAmount)\n .sum();\n }\n}\n\nApenas três linhas. Sem variáveis mutáveis, sem loops. Apenas dizemos: "Pegue o fluxo, filtre pelo ano, pegue os valores, some". Este é o estilo declarativo.
\n\nComparação: onde cada um vence?
\n\nVamos detalhar em quais cenários cada abordagem se sai melhor.
\n\n1. Legibilidade e concisão
\nVencedor: Abordagem funcional