Enfoque funcional vs orientado a objetos en Java: ¿qué elegir para el desarrollador moderno?
\n\nEl mundo de Java es un campo de batalla de dos gigantes. Por un lado, la clásica programación orientada a objetos (POO), en la que creció toda una generación de desarrolladores. Por el otro, el creciente enfoque funcional, que irrumpió en el ecosistema con el lanzamiento de Java 8 y aún persiste el debate: «¿Realmente lo necesitamos?».
\n\nSi programas en Java, te enfrentas a esta elección a diario. ¿Vale la pena crear una clase completa con métodos para un simple filtrado? ¿O es mejor encadenar lambdas y olvidarse? En este artículo no tomaremos partido. Analizaremos ambos enfoques con ejemplos reales, los compararemos según métricas clave y te ayudaremos a entender cuándo usar cada herramienta de tu arsenal.
\n\nBreve repaso: cómo llegamos hasta aquí
\n\nJava fue concebido originalmente como un lenguaje puramente orientado a objetos. Todo es un objeto. Incluso una cadena es un objeto de la clase String. Clases, herencia, polimorfismo: esa era la religión. Pero con el tiempo, los desarrolladores se dieron cuenta: la POO modela muy bien el mundo real (máquinas, empleados, pedidos), pero es terrible para procesar flujos de datos.
Entonces entraron en escena las expresiones lambda y la Stream API. Java no se convirtió en un lenguaje funcional (como Haskell o Scala), pero tomó lo mejor de este paradigma. Ahora tenemos la opción: escribir un bucle for dentro de un método de clase o usar filter().map().collect().
Enfoque orientado a objetos: el clásico del género
\n\nLa POO en Java se basa en cuatro pilares: encapsulamiento, herencia, polimorfismo y abstracción. Cuando trabajas con lógica de negocio que tiene entidades (Usuario, Pedido, Producto), la POO es la elección natural.
\n\nAquí hay un ejemplo de una tarea clásica: calcular la suma de todos los pedidos de un cliente realizados en 2024.
\n\nSolución 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\nTodo es transparente, legible, fácil de depurar. Cualquier junior entenderá lo que sucede aquí. Pero hay un detalle: el código es imperativo. Decimos cómo hacerlo (crea una variable, recorre la lista, verifica la condición, suma).
\n\nEnfoque funcional: escribimos de forma declarativa
\n\nLa programación funcional en Java se trata de qué queremos obtener, no de cómo. Usamos funciones de orden superior, inmutabilidad y ausencia de efectos secundarios.
\n\nLa misma tarea, pero en estilo funcional:
\n\nSolución 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\nSolo tres líneas. Sin variables mutables, sin bucles. Simplemente decimos: «Toma el flujo, filtra por año, toma los montos, suma». Este es el estilo declarativo.
\n\nComparación: ¿dónde gana cada uno?
\n\nAnalicemos en qué escenarios cada enfoque se desempeña mejor.
\n\n1. Legibilidad y concisión
\nGanador: Enfoque funcional