Ir al contenido principal

Java Tip 23: functional interfaces

En la anterior entrega vimos lo que son las expresiones lambda. En esta ocasión veremos las Functional Interfaces.

Una Functional Interface es una interfaz Java que contiene un solo método abstracto. Esto garantiza que se pueda usar con expresiones lambda.

¿Cómo definimos una Functional Interface (interface funcional)? Con la anotación @FunctionalInterface un ejemplo sería este bloque:

@FunctionalInterface
interface MyInterfaceFuncional {
    void hacerAlgo();
}

Como vimos anteriormente existen cuatro principales:

  1. Consumer: Puede o no recibir algún valor y no devolver nada. 
  2. Function: Puede tomar dos valores de distinto tipo y devolver un valor en específico. 
  3. Predicate: Puede tomar valores y devolver un valor booleano true o false. 
  4. Supplier: No recibe valores, pero devuelve un valor.

Consumer.

@FunctionalInterface
interface Consumer<T> {
    void accept(T t);
}

Function.

@FunctionalInterface
interface Function<T, R> {
    R apply(T t);
}

Predicate.

@FunctionalInterface
interface Predicate<T> {
    boolean test(T t);
}

Supplier.

@FunctionalInterface
interface Supplier<T> {
    T get();
}

Para usarlas, es necesario importarlas:

import java.util.function.Consumer;//interface
import java.util.function.Function;//interface
import java.util.function.Supplier;//interface
import java.util.function.Predicate;//interface

Ejemplo 1. Crear una Interface funcional que sume 3 números tipo Integer.

@FunctionalInterface
interface Suma {
    Integer sumar(Integer x, Integer y , Integer z);
}

Su implementación sería la siguiente:

Suma operacion = (a, b, c) -> a +  b + c;
Integer a, b, c; a = 23; b = 3; c = 4; Integer resultado = operacion .sumar(a,b,c);

Interfaces funcionales y referencias a métodos

Las referencias a métodos proporcionan una notación abreviada para las expresiones lambda que invocan un solo método. Ofrecen una forma alternativa de representar las expresiones lambda de forma más concisa y expresiva. Son especialmente útiles cuando el único propósito de la expresión lambda es invocar un método existente.

Los 4 tipos de referencia a métodos:

  1. Referencia a un método estático: `ClassName::staticMethodName`. 
  2. Referencia a un método de instancia de un objeto específico: `instance::instanceMethodName`. 
  3. Referencia a un método de instancia de un objeto arbitrario de un tipo específico: `ClassName::instanceMethodName`. 
  4. Referencia a un constructor: `ClassName::new`.

Ejemplo 2. De esta clase hacer uso de una referencia a método estático.

package com.inforhomex;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class TestLambdas{

   public static void main(String[] args){
         List<String> nombres = new ArrayList<String>(); 
         nombres.add("Tomas");
         nombres.add("Azucena");
         nombres.add("Leopoldo");
         nombres.add("Miguel");
         nombres.add("Berenice");
         nombres.add("Ricardo");

          System.out.println("\n--- Usando Consumer ---");
          Consumer<String> consumir = (n) -> { System.out.printf("Hola, %s\n",n); };
          nombres.forEach(consumir);
      
   }

}

Resultado:

package com.inforhomex;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class TestLambdas {

    // Método estático que será referenciado
    public static void saludar(String nombre) {
        System.out.printf("Hola, %s\n", nombre);
    }

    public static void main(String[] args) {
        List<String> nombres = new ArrayList<String>();
        nombres.add("Tomas");
        nombres.add("Azucena");
        nombres.add("Leopoldo");
        nombres.add("Miguel");
        nombres.add("Berenice");
        nombres.add("Ricardo");

        System.out.println("\n--- Usando Consumer con referencia a método estático ---");

        // Referencia al método estático
        Consumer<String> consumir = TestLambdas::saludar;

        // Aplicar el Consumer a cada elemento
        nombres.forEach(consumir);
    }
}

Ventajas de Expresiones Lambda en Java:

  • Código más conciso y legible: Evitan clases anónimas largas. 
  • Menos boilerplate: Se escribe solo la lógica, no toda la estructura. 
  • Mayor expresividad: El código refleja directamente la intención (ej. `list.forEach(x -> ...)`). 
  • Facilitan programación funcional: Se integran con Streams y APIs modernas. 
  • Mejor mantenimiento: Más fácil de leer y modificar.

Ventajas de Functional Interfaces:

  • Definen contratos claros: Una sola operación abstracta (ej. `Consumer`, `Predicate`, `Function`). 
  • Compatibles con lambdas y referencias de método: Se usan como destino natural de expresiones lambda. 
  • Reutilización: Interfaces estándar en `java.util.function` evitan reinventar la rueda. 
  • Flexibilidad: Permiten pasar comportamiento como parámetro (ej. estrategia, filtros, transformaciones). 
  • Integración con APIs modernas: Streams, Optional, CompletableFuture, etc.

En conclusión: Las expresiones lambdas simplifican la escritura de lógica y las functional interfaces permiten tratar el comportamiento como datos, lo que hace el código más limpio, flexible y poderoso.

Enlaces:

https://medium.com/@nagarjun_nagesh/functional-interfaces-in-java-fe5ebf5bafed
https://alquimistadecodigo.blogspot.com/2024/11/expresiones-lambda.html

Comentarios

Entradas populares de este blog

Java Tip 1: Importaciones por default

Tip 1 : Todas las clases del paquete java.lang se importan por defecto. Podríamos hacer algo como esto: import java.lang.System ; import java.lang.String ; import java.lang.Boolean ; import java.lang.Integer ; import java.lang.Short ; import java.lang.StringBuilder ; import java.lang.StringBuffer ; O cosas (aberrantes) como: //Traernos todas las clases del paquete java.lang import java.lang.* ; Sin embargo, no es necesario. Repitamos esto como un mantra: Todas las clases del paquete  java.lang  se importan por defecto, no es necesario escribir de más .

Java Tip 28: hilos virtuales en Java

Con Java 21 entra un nuevo concepto: hilos virtuales java ( Java Virtual Threads ).  La cual es una nueva característica que nos permite crear miles o millones de hilos (tareas en paralelo). Los cuales se diferencian de los hilos comunes ( Platform Threads ) al no depender del sistema operativo , puesto que son virtuales. De estos se encargará la JVM. Al usar hilos virtuales ahorramos recursos del procesador y de memoria del sistema , sin importar si estamos creando y/o usando miles o millones. Los hilos virtuales son ideales para aplicaciones modernas donde se requiere esperar y recibir respuestas o acceder a bases de datos remotas, entre otras cosas. Además no impide usar los hilos comunes que dependen del sistema operativo. Observemos un ejemplo de uso de hilos virtuales. VirtualThreadsDemo.java package com.comunidad.demo; public class VirtualThreadsDemo { public static void main (String [] args) throws InterruptedException { // Crear un Virtual Th...

Java Tip 20: JDBC (2da parte)

Continuamos con esta serie sobre JDBC . Como dijimos la vez anterior JDBC( Java Database Connectivity ) es el estándar de conectividad de bases de datos de Java y proporciona un mecanismo para que los programas Java se conecten a las bases de datos. En este post veremos ejemplos de su uso. Requisitos: Tener nociones de Java.  Tener nociones de SQL (usaremos H2). Tener nociones de Maven. Creando una aplicación JDBC Crearemos una sencilla aplicación que se conecte a una BD H2 (ver  tutorial ). Pasos: Crearemos una tabla a la que llamaremos Cursos. La cual tendrá los siguientes campos: id, titulo, materia, instructor, fecha y hora. Crear la aplicación Java usando Maven. Crearemos instrucciones SQL para consultar la información, ingresar nuevos cursos, actualizar y eliminar registros. Ejecutar la aplicación con Maven. 1. Creamos la tabla con sus seis campos (agregaremos los datos con la aplicación Java): CREATE OR REPLACE TABLE CURSOS( id bigint ...