Entendiendo paso a paso las expresiones Lambda en Java 8

expresiones lambda en java

Tutorial de expresiones Lambda en Java 8 paso a paso

Hola que tal, esta vez vamos a ver las expresiones lambda una de las novedades más importantes que se ha tenido para la versión Java 8, la idea de las expresiones lambda es tener un código más limpio y legible, y aunque para programadores de otros lenguajes como C#, JavaScript, Python ya son utilizadas, para nosotros los programadores Java esto es nuevo.

En esta entrada trato de explicar cuales son los fundamentos de las Expresiones Lambda en Java. Si deseas ver ejemplos más prácticos, por ejemplo como hacer consultas sobre una lista, obtener el máximo, mínimo, o hacer algún filtro utilizando alguna condición puedes ir directamente a esta entrada Ejemplos prácticos de Expresiones Lambda.

Bien, antes de empezar a ver todo el trasfondo y cambios que tuvo la implementación  de las expresiones lambda en Java veamos un ejemplo muy sencillo recorriendo una lista de números.

Recorrer una lista de números en versiones anteriores  de Java

Recorrer una lista de números utilizando expresiones Lambda en Java

Veamos otro ejemplo tenemos almacenados números del 1 al 10 en una lista y queremos obtener los números mayores a 5 y almacenarlos en una nueva lista.

En versiones anteriores de Java

Y utilizando expresiones Lambda.

Utilizando expresiones Lambda en Java 8

Como se puede ver el cambio entre versiones anteriores y Java 8 es bastante bueno de hecho se puede hacer en 2 líneas de código  todo lo que en versiones anteriores se lo hace entre 9 y 12 líneas.

Bueno esto es muy bueno, pero no todo es así de sencillo tras de esto hay algunas cosas que se debe tener claro para comprender las expresiones Lambda.

Qué es una expresión Lambda?

Una expresión Lambda es una función anónima, básicamente es un método abstracto es decir un método que sólo está definido en una interfaz pero no implementado, y esa es la clave de la funciones lambda, al no estar implementado, el programador lo puede implementar dónde el crea conveniente sin haber heredado de la interfaz.

Si te diste cuenta en el primer ejemplo se puede imprimir de dos formas una lista, la primera es forEach(n -> System.out.print(n + » «), mientras que la segunda es forEach(System.out::println), en los dos casos este método acepta expresiones Lambda, yendo más a fondo lo que acepta como parámetro es un Consumer<Tipo>, un consumidor que lo veremos a fondo en lo que viene del tutorial .

Bien tal vez te suena un poco confuso y más aún si no has leído o nos tienes muchos conocimientos de la POO en Java, por eso te pido leas el tema de interfaces, clases abstractas, herencia de manera que te enganches de mejor manera al tema de Lambdas

La sintaxis de un expresión suele darse de la siguiente forma:

(argumentos)->{cuerpo}

Por ejemplo:

(arg1, arg2…) -> { cuerpo}

Esta sintaxis puede cambiar tanto como para los argumentos o el cuerpo de la expresión Lambda  de acuerdo a lo siguiente.

Para los argumentos:

Los argumentos de una función Lambda pueden ser declarados explícitamente o a su vez pueden ser inferidos por el compilador de acuerdo al contexto, cuando digo de acuerdo al contexto, me refiero a que si el método que estamos implementado recibe una cadena el compilador asumirá que el argumento es una cadena y así con el tipo de dato que estuviéramos recibiendo en el método.

Entonces un argumento se puede declarar explícitamente, esto se refiere al tipo de dato, por ejemplo: (int x)-> {cuerpo} y si tiene más parámetros sería (int x, int y….)-> {cuerpo}.

También se lo puede hacer de forma implícita por ejemplo: (x)-> {cuerpo}, de echo si existe un sólo argumento y se lo declara de forma implícita puede ir incluso sin paréntesis por ejemplo x -> {cuerpo}, si se declara más de un parámetro obligatoriamente deben ir seguido de comas y entre paréntesis.

Por último puede haber expresiones Lambda en las que no hayan argumentos y se expresan de la siguiente forma: ()-> {cuerpo}.

Para el cuerpo de la expresión:

El cuerpo de la expresión puede ir o no dentro de llaves esto puede variar como se ve en los siguientes ejemplos.

Es obligatorio que el cuerpo de una expresión vaya entre llaves en los siguientes casos:

Cuando devuelve más de un valor o la sentencia tiene más de una instrucción por ejemplo:

(int a, int b) -> {  return a + b; }.

() -> { return 3.1415 }.

Mientras que cuando devuelve un sólo valor no es obligatorio, de todas maneras el compilador no muestra error si se pone entre llaves por ejemplo, (aunque hay algunas excepciones):

() -> 10

n -> System.out.print(n + » «)

Pero se la puede encerrar dentro de llaves, para esto es necesario añadir al final de la sentencia un punto y coma:

n -> {System.out.print(n + » «);}

() -> new ArrayList<Integer>().

Que es una interfaz funcional?

Bien, las expresiones Lambda van de la mano con las interfaces funcionales, el concepto de interfaz funcional es añadido con la versión de Java 8 dada la necesidad de las expresiones Lambda.

Una interfaz funcional guarda el mismo concepto que una interfaz en las anteriores versiones de Java, salvo que se añade 2 reglas y es que para que una interfaz sea funcional debe:

  1. Tener un sólo método abstracto.
  2. Una interfaz funcional debe implementar los métodos dentro la misma interfaz (esto no se podía hacer en versiones anteriores), para esto se debe anteponer la palabra reservada default al inicio de la declaración del método.

Nota: Claro está que sólo un método debe ser abstracto, si la interfaz tuviera más métodos estos deben implementarse dentro de la misma.

Así mismo aunque es opcional se puede declarar la anotación @FunctionalInterface, esta anotación le indica al compilador que esta es una interfaz funcional.

Veamos un ejemplo sencillo, una interfaz funcional que declara un método para sumar dos números enteros y que retorna su valor, si te das cuenta el método sólo se declara.

Ahora viene la implementación del método sumar, en este caso los parámetros está declarados de forma implícita (a, b) de manera que el compilador empareje el tipo de dato con el que se encuentra en el método sumar.

Luego tenemos el cuerpo que es la implementación del método y que viene a ser { System.out.println(a + b); }.  

En conclusión si te das cuenta una expresión Lambda puede utilizarse donde haya una interfaz funcional que tenga la declaración del método que utiliza la expresión.

Las expresiones Lambda se pueden dividir de la siguiente manera:

Predicados

Los predicados son expresiones que reciben un argumento y devuelven un valor lógico por ejemplo, se usa la interface Predicate<T>:

En el siguiente ejemplo a partir de una lista de números enteros se imprime: los números pares, los números mayores a 5 y los impares.

Funciones

Las funciones reciben un argumento y devuelven un resultado, usan la interface Function<T,R>, revisemos un ejemplo sencillo.

Podemos también encontrar el tamaño de una cadena por ejemplo:

Proveedores

Las expresiones Lambda de este tipo no tiene parámetros de entrada, pero si devuelven un resultado, utilizan la interface Supplier<T>.

Veamos un ejemplo sencillo, que básicamente obtiene la cadena enviada a la interface funcional, a través de una expresión Lambda tipo proveedor.

Un ejemplo un poco más detallado, primero creamos una clase Persona.

Se implementa la expresión Lambda que es de tipo proveedor utilizando la clase Supplier.

Consumidor

Utilizan la interfaz Consumer<T>, tienen un sólo argumento de entrada y no devuelven ningún valor, en este ejemplo se usa la misma clase Persona que se utilizó en el ejemplo de tipo proveedor.

REFERENCIA A MÉTODOS

Con esta funcionalidad no sólo se puede utilizar expresiones lambda sino que se puede hacer referencia a los métodos del objeto utilizando el operador ::, existen 3 tipos:

Nota: Para estos ejemplos se utilizó la clase Usuario que se encuentra en la parte de Resumen expresiones Lambdas que viene luego de este tema.

Métodos estáticos

En este ejemplo se utiliza el método System.out::println, como referencia a métodos estáticos.

Métodos de instancia

En este ejemplo se utiliza el método toString() que fue redefinido en la clase Usuario.

Referencia a mensajes

En este ejemplo se utiliza El método removeLast para eliminar el último elemento de la lista por último, se imprime la lista.

Referencia a constructores

En este ejemplo se utiliza el operador  new para crea una referencia a un objeto de tipo Usuario.

Resumen expresiones Lambdas

Básicamente estos son los fundamentos para aprender las expresiones Lambda en Java, si quieres obtener información más a fondo puedes consultar la página oficial Lambda Expressions o también Paquete de Interfaces Funcionales.

Espero que este tutorial te haya servido, házmelo saber en los comentarios, nos vemos en la próxima entrada.

Opt In Image
Programación Web Full Stack
Suscríbete ahora y recibe los mejores contenidos sobre Programación Web en tu correo.

Tus datos estarán protegidos y 100% libre de Spam

Mi nombre es Elivar Largo, Developer Full Stack, blogger y emprendedor. Trabajo y comparto conocimientos sobre las siguientes tecnologías: Spring Boot, Laravel, Angular, Flutter. Contacto: elargor@gmail.com.

6 comentarios en «Entendiendo paso a paso las expresiones Lambda en Java 8»

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

  +  35  =  37

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.