Lambdas y referencias a métodos (::)

La base de la programación funcional en Java 8

De clases anónimas a lambdas

Antes de Java 8, pasar comportamiento como argumento requería clases anónimas: mucho boilerplate para expresar algo simple. Las lambdas eliminan ese ruido.

// Antes de Java 8: clase anónima
List<String> nombres = Arrays.asList("Carlos", "Ana", "Luis");

Collections.sort(nombres, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
});
// Con lambda: misma operación, sin el boilerplate
Collections.sort(nombres, (a, b) -> a.compareTo(b));

// Aún más corto con referencia a método
Collections.sort(nombres, String::compareTo);

Sintaxis de las lambdas

Una lambda es una función anónima: (parámetros) -> cuerpo. El tipo de los parámetros se infiere del contexto — el compilador sabe qué interfaz funcional se espera y deduce los tipos.

// Formas de lambda según el número de parámetros y el cuerpo

// Sin parámetros
Runnable r = () -> System.out.println("ejecutando");

// Un parámetro (los paréntesis son opcionales)
Consumer<String> imprimir = s -> System.out.println(s);

// Varios parámetros
Comparator<Integer> comp = (a, b) -> a - b;

// Cuerpo con varias sentencias: necesita llaves y return explícito
Function<String, String> procesar = texto -> {
    String limpio = texto.trim();
    return limpio.toLowerCase();
};

Referencias a métodos (::)

Una referencia a método es una lambda que no hace nada más que llamar a un método ya existente. Es más legible cuando el cuerpo de la lambda sería simplemente una llamada directa.

// Cuatro tipos de referencia a método

// 1. Método estático: Clase::metodoEstatico
Function<String, Integer> parse = Integer::parseInt;

// 2. Método de instancia sobre un objeto concreto: objeto::metodo
String prefijo = "Hola, ";
Function<String, String> saludar = prefijo::concat;

// 3. Método de instancia sobre el parámetro: Clase::metodoInstancia
Function<String, String> mayus = String::toUpperCase;
Predicate<String> vacio = String::isEmpty;

// 4. Constructor: Clase::new
Supplier<ArrayList<String>> fabrica = ArrayList::new;

Ejemplo integrado

// Ejemplo integrado: filtrar, transformar e imprimir una lista
List<String> nombres = Arrays.asList("Ana", "luis", "EVA", "carlos");

nombres.stream()
    .map(String::toLowerCase)        // referencia a método de instancia
    .filter(n -> n.startsWith("a"))  // lambda predicado
    .forEach(System.out::println);   // referencia a método estático de instancia

// Salida: ana
Regla práctica: usa lambda cuando el cuerpo tiene lógica propia. Usa referencia a método (::) cuando el cuerpo es simplemente la llamada a un método que ya existe — el código queda más declarativo y es más fácil de leer.

Siguiente apartado → Interfaces funcionales: Predicate, Function, Consumer y Supplier

Índice de la sección

Índice del curso