Programación Funcional en Java

Expresiones lambda

Las expresiones lambda en Java te permiten pasar funciones como argumentos a métodos o construir instancias de interfaces funcionales de forma concisa y elegante.

Sintaxis Básica

Una expresión lambda tiene la siguiente sintaxis básica:

				
					(parametros) -> { cuerpo de la expresion lambda }

				
			
  • parametros: Los parámetros de la función.
  • cuerpo de la expresion lambda: El cuerpo de la función.

Ejemplo de Uso

				
					public class LambdaEjemplo {
    public static void main(String[] args) {
        // Expresión lambda que suma dos números
        Operacion suma = (a, b) -> a + b;
        
        // Llamada a la expresión lambda
        int resultado = suma.operar(5, 3);
        System.out.println("Resultado de la suma: " + resultado);
    }
}

// Interfaz funcional
interface Operacion {
    int operar(int a, int b);
}

				
			

En este ejemplo:

  • Se define una expresión lambda que implementa la interfaz funcional Operacion.
  • La expresión lambda suma dos números.
  • Se llama a la expresión lambda y se imprime el resultado.

Ventajas de las Expresiones Lambda

  • Concisión: Reducción de código y sintaxis más clara en comparación con las clases anónimas.
  • Legibilidad: Facilita la comprensión del código al enfocarse en lo que se hace y no en cómo se hace.
  • Flexibilidad: Permite pasar comportamiento como argumentos a métodos de manera más flexible.

Interfaz funcional

Una interfaz funcional en Java es una interfaz que contiene un único método abstracto. Se utilizan principalmente para implementar expresiones lambda y referencias a métodos.

Definición

				
					@FunctionalInterface
public interface MiInterfazFuncional {
    void miMetodo(); // Método abstracto
}

				
			

@FunctionalInterface: Es una anotación opcional que indica que la interfaz es funcional. No es obligatoria, pero es una buena práctica usarla para evitar agregar accidentalmente métodos abstractos adicionales.

Uso

				
					public class InterfazFuncionalEjemplo {
    public static void main(String[] args) {
        // Implementación de la interfaz funcional mediante una expresión lambda
        MiInterfazFuncional miFuncion = () -> {
            System.out.println("Hola desde mi método funcional");
        };
        
        // Llamada al método de la interfaz funcional
        miFuncion.miMetodo();
    }
}

				
			

En este ejemplo, MiInterfazFuncional es una interfaz funcional con un único método abstracto miMetodo(). Se implementa utilizando una expresión lambda.

Ventajas

  • Simplicidad: Permite crear comportamiento a demanda de manera simple y concisa.
  • Flexibilidad: Facilita el uso de funciones como argumentos en métodos.
  • Legibilidad: Mejora la claridad del código al enfocarse en lo que se hace y no en cómo se hace.

Streams API

La Streams API en Java proporciona una forma elegante y funcional de procesar colecciones de datos de manera eficiente.

Introducción

Los streams representan una secuencia de elementos y proporcionan operaciones para realizar transformaciones en estos elementos de forma declarativa.

Creación de Streams

Puedes crear streams a partir de colecciones, arrays o directamente.

				
					import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamsEjemplo {
    public static void main(String[] args) {
        // Creación de un stream a partir de una lista
        List<String> lista = Arrays.asList("uno", "dos", "tres");
        Stream<String> streamDeLista = lista.stream();
        
        // Creación de un stream a partir de un array
        String[] array = {"cuatro", "cinco", "seis"};
        Stream<String> streamDeArray = Arrays.stream(array);
        
        // Creación de un stream directamente
        Stream<Integer> numerosStream = Stream.of(1, 2, 3, 4, 5);
    }
}

				
			

Operaciones sobre Streams

Puedes realizar diversas operaciones sobre streams, como filtrado, mapeo, reducción, etc.

				
					import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamsOperaciones {
    public static void main(String[] args) {
        List<String> lista = Arrays.asList("uno", "dos", "tres", "cuatro", "cinco");

        // Filtrado
        List<String> filtrados = lista.stream()
                                     .filter(s -> s.startsWith("c"))
                                     .collect(Collectors.toList());
        
        // Mapeo
        List<Integer> longitudes = lista.stream()
                                       .map(String::length)
                                       .collect(Collectors.toList());
        
        // Reducción
        int suma = lista.stream()
                       .mapToInt(String::length)
                       .sum();
    }
}

				
			

Características

  • Declarativo: Permite expresar las operaciones de forma más clara y concisa.
  • Eficiente: Se puede utilizar para procesar grandes cantidades de datos de manera eficiente utilizando operaciones en paralelo.
  • Composición: Se pueden encadenar múltiples operaciones para realizar un procesamiento complejo en una sola línea.