java-interfaces-funcionales-predicate-consumer-function-supplier

Interfaces funcionales: Predicate, Consumer, Function y Supplier

  • 3 min

Una interfaz funcional es una interfaz con un único método abstracto que puede implementarse mediante una lambda.

En el artículo anterior aprendimos a escribir flechas (->) para expresar comportamientos breves.

Pero, si yo escribo: s -> s.length()

¿De qué tipo es eso? ¿En qué variable lo guardo? Antes de Java 8, teníamos que inventarnos interfaces propias como Calculadora o Imprimible. Pero Java 8 dijo: “Basta de reinventar la rueda. Vamos a crear un estándar”.

Así nació el paquete java.util.function. Aquí viven varias interfaces predefinidas que cubren muchos casos habituales. Nos centraremos en cuatro: Predicate, Consumer, Function y Supplier.

Predicate <T>

El Predicado sirve para evaluar una condición. Toma un objeto y dice SI o NO (true/false).

  • Recibe: Un objeto T.
  • Devuelve: boolean.
  • Método abstracto: test(T t).
// Definición: Un predicado que decide si un String es largo
Predicate<String> esLargo = texto -> texto.length() > 5;

// Uso:
boolean resultado = esLargo.test("Hola Mundo"); // true
boolean resultado2 = esLargo.test("Java");      // false
Copied!

Uso Real: Es el rey del método .filter() en los Streams (filtrar listas).

Consumer <T>

El Consumidor sirve para ejecutar una acción sobre un objeto. Se traga el objeto, hace algo con él (imprimirlo, guardarlo en BD, enviarlo) y no devuelve nada.

  • Recibe: Un objeto T.
  • Devuelve: void (Nada).
  • Método abstracto: accept(T t).
// Definición: Un consumidor que imprime en mayúsculas
Consumer<String> gritador = texto -> System.out.println(texto.toUpperCase());

// Uso:
gritador.accept("hola"); // Imprime: "HOLA"
Copied!

Uso Real: Es el rey del método .forEach() (recorrer listas).

Function <T, R>

La Función sirve para convertir una cosa en otra. Toma una materia prima (T) y devuelve un producto procesado (R).

  • Recibe: Un objeto T.
  • Devuelve: Un objeto R (Resultado).
  • Método abstracto: apply(T t).
// Definición: Transforma un String (T) en su longitud Integer (R)
Function<String, Integer> longitud = texto -> texto.length();

// Uso:
Integer largo = longitud.apply("Java"); // 4
Copied!

Uso Real: Es el rey del método .map() (transformar datos).

Supplier <T>

El Proveedor (o Supplier) sirve para generar valores de la nada. No recibe nada, pero te entrega algo. Es como una tubería de la que salen objetos.

  • Recibe: Nada ().
  • Devuelve: Un objeto T.
  • Método abstracto: get().
// Definición: Genera un número aleatorio
Supplier<Double> aleatorio = () -> Math.random();

// Uso:
Double n = aleatorio.get();
Copied!

Uso Real: Se usa mucho en inicialización perezosa (Lazy) y en Optional.orElseGet() (crear un objeto por defecto solo si hace falta).

Variantes “bi” y primitivas

A veces necesitamos funciones que reciban dos argumentos. Java añade el prefijo Bi.

  • BiFunction<T, U, R>: Recibe T y U, devuelve R. (Ej: Sumar dos números).
  • BiConsumer<T, U>: Recibe T y U, no devuelve nada. (Ej: Poner clave-valor en un Mapa).
  • BiPredicate<T, U>: Recibe T y U, devuelve boolean.

Y para evitar el coste de los Wrappers (Integer), hay versiones para primitivos: IntPredicate, LongFunction, etc.