Ir al contenido principal

Java Tip 22: expresiones lambda

Hoy hablaremos de una característica fundamental del lenguaje Java que apareció desde la versión 8: las expresiones lambda.

Las cuales no son otra cosa que funciones que no necesitan pertenecer a alguna clase previamente definida, es decir, son anónimas.

Características:

  • Vienen de la programación funcional. 
  • Son funciones anónimas. 
  • Se escriben en línea. 
  • Pueden o no recibir cero o más argumentos. 
  • Pueden o no retornar algún valor.
  • Pueden consumir métodos de otras clases y objetos.
  • Pueden pasar como argumentos en los métodos.
  • Solo pueden acceder a variables del ámbito al que pertenecen.

La sintaxis básica es la siguiente:

() => {cuerpo-lambda}
(param) => {cuerpo-lambda}
(param1, param2) => {cuerpo-lambda}
(param1, param2, paramN) => {cuerpo-lambda}

Miremos unos ejemplos:

Sin parámetros:

() -> System.out.println("Hola desde una lambda sin parámetros");

Con un parámetro:

Predicate<Integer> esPar = x -> x%2 ==0;

En este ejemplo usamos la interface ``Predicate``.

Existen 4 principales expresiones lambda (functional interfaces):

  1. Consumidores (Consumer), los consumidores.
  2. Proveedores (Supplier), los proveedores. 
  3. Funciones(Function), las funciones: unarios y binarios 
  4. Predicados(Predicate), los predicados.

Para poder usarlos es necesario importar el paquete:

import java.util.function.*;

O de esta forma más correcta:

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

¿Para qué nos sirven estas 4 expresiones lambda (funtional interfaces)?

  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. 
  4.  Supplier: No recibe valores, pero devuelve un valor.

Veamos un ejemplo con el ``Consumer``, que es una interface que que acepta un tipo determinado y realiza una operación, esto sin devolver nada. Tenemos una clase llamada TestLambdas.java`y dentro de esta tenemos un bloque que contiene una lista de tipo String y queremos recorrerla con un "for each":

package com.inforhomex;

import java.util.ArrayList;
import java.util.List;

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");

      /***  Recorrer con "for each" ****/
      for(String nombre: nombres){
         System.out.printf("Hola, %s\n", nombre);
      }
      
   }

}

¿Cómo usaríamos la interface ``Consumer`` en este ejmplo?

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);
      
   }

}

La interface ``Consumer`` nos ha servido para mostrar "de una forma más limpia" los items de la lista tipo String.

Usando la interface ``Function``. Esta interface recibe un valor y lo devuelve transformado. Miremos este ejemplo:

package com.inforhomex;

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

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 Function ---");
        Function<String, String> aMayusculas = (n) -> n.toUpperCase();
        nombres.stream()
               .map(aMayusculas)
               .forEach(n -> System.out.println("En mayúsculas: " + n));

    }
}

Nos devuelve cada uno de los nombres en mayúscula.

Usando la interface ``Predicate``. Esta interface recibe un parámetro y nos devuelve una salida en true o false. Miremos el ejemplo:

package com.inforhomex;

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

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 Predicate ---");
        Predicate<String> empiezaConA = (n) -> n.startsWith("A");
        nombres.stream()
               .filter(empiezaConA)
               .forEach(n -> System.out.println("Nombre con A: " + n));

    }
}

Solo mostrará los nombres que empiezan con la letra 'A'.

Usando la interface ``Supplier``. Está interface no recibe ningún valor, pero si devolverá algo.

package com.inforhomex;

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

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 Supplier ---");
        Supplier<String> proveedor = () -> "Michael";
        System.out.println(proveedor.get());
    }
}

En este caso devolverá un nombre.

Código completo:

TestLambdas.java

package com.inforhomex;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

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);

        System.out.println("\n--- Usando Predicate ---");
        Predicate<String> empiezaConA = (n) -> n.startsWith("A");
        nombres.stream()
               .filter(empiezaConA)
               .forEach(n -> System.out.println("Nombre con A: " + n));

        System.out.println("\n--- Usando Function ---");
        Function<String, String> aMayusculas = (n) -> n.toUpperCase();
        nombres.stream()
               .map(aMayusculas)
               .forEach(n -> System.out.println("En mayúsculas: " + n));

        System.out.println("\n--- Usando Supplier ---");
        Supplier<String> proveedor = () -> "Michael";
        System.out.println(proveedor.get());
    }
}

Este patrón es muy útil cuando trabajas con Streams y colecciones, porque te permite filtrar, transformar y consumir datos de manera declarativa.

Las expresiones lambdas vuelven a Java un lenguaje más moderno implementando conceptos de la programación funcional.

Enlaces:

https://www.arteco-consulting.com/articulos/introduccion-java-lambda/
https://codemonkeyjunior.wordpress.com/2019/04/09/java-lambdas/
https://codemonkeyjunior.wordpress.com/2018/04/23/un-vistazo-a-lambdas-java-8/
https://codemonkeyjunior.wordpress.com/2019/03/19/java-8-java-util-function/
https://asjordi.dev/blog/pasar-funciones-lambda-como-argumentos-de-metodos-en-java/

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 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 ...

Lo nuevo de Java desde la versión 12 hasta la 22

Aquí unos enlaces a varias publicaciones sobre algunos nuevos aspectos del lenguaje Java, desde la versión 12 a la 22. Expresiones Switch en Java Una nueva funcionalidad en el lenguaje: las expresiones switch. https://emanuelpeg.blogspot.com/2025/01/expresiones-switch-en-java.html API de Servidor Web Simple en Java Una API de Servidor Web Simple que permite crear servidores HTTP ligeros sin necesidad de dependencias externas. https://emanuelpeg.blogspot.com/2025/02/api-de-servidor-web-simple-en-java.html Clases Record Una implementación de las data-clases o del patrón Data Transfer Object, un tipo de clase cuyo único propósito es impulsar la programación con datos inmutables. https://alquimistadecodigo.blogspot.com/2025/02/java-clases-record.html https://emanuelpeg.blogspot.com/2025/02/record-en-java.html Clases Selladas y coincidencia de patrones en Java https://emanuelpeg.blogspot.com/2025/03/clases-selladas-y-coincidencia-de.html https://emanuelpeg.blogspo...