Cómo empezar a trabajar con expresiones Lambda en Java

Antes de que JDK 8 agregara el soporte de expresiones Lambda, solo había usado ejemplos de ellas en lenguajes como C # y C ++.

Una vez que esta función se agregó a Java, comencé a analizarlas un poco más de cerca.

La adición de expresiones lambda agrega elementos de sintaxis que aumentan el poder expresivo de Java. En este artículo, quiero centrarme en los conceptos fundamentales con los que debe familiarizarse para que pueda comenzar a agregar expresiones lambda a su código hoy.

Introducción rápida

Las expresiones Lambda aprovechan las capacidades de procesos paralelos de entornos de múltiples núcleos como se ve con el soporte de operaciones de canalización en datos en la API de Stream.

Son métodos anónimos (métodos sin nombres) que se utilizan para implementar un método definido por una interfaz funcional. Es importante saber qué es una interfaz funcional antes de ensuciarse las manos con expresiones lambda.

Interfaz funcional

Una interfaz funcional es una interfaz que contiene un solo método abstracto.

Si se echa un vistazo a la definición de la interfaz Java estándar Ejecutable, se dará cuenta de cómo se cae en la definición de interfaz funcional, ya que sólo define un método: run().

En el siguiente ejemplo de código, el método computeNamees implícitamente abstracto y es el único método definido, lo que convierte a MyName en una interfaz funcional.

interface MyName{ String computeName(String str); }

El operador de flecha

Las expresiones Lambda introducen el nuevo operador de flecha ->en Java. Divide las expresiones lambda en dos partes:

(n) -> n*n

El lado izquierdo especifica los parámetros requeridos por la expresión, que también podrían estar vacíos si no se requieren parámetros.

El lado derecho es el cuerpo lambda que especifica las acciones de la expresión lambda. Puede resultar útil pensar en este operador como "se convierte". Por ejemplo, "n se convierte en n * n" o "n se convierte en n al cuadrado".

Teniendo en cuenta la interfaz funcional y los conceptos del operador de flecha, puede armar una expresión lambda simple:

interface NumericTest { boolean computeTest(int n); } public static void main(String args[]) { NumericTest isEven = (n) -> (n % 2) == 0; NumericTest isNegative = (n) -> (n < 0); // Output: false System.out.println(isEven.computeTest(5)); // Output: true System.out.println(isNegative.computeTest(-5)); }
interface MyGreeting { String processName(String str); } public static void main(String args[]) { MyGreeting morningGreeting = (str) -> "Good Morning " + str + "!"; MyGreeting eveningGreeting = (str) -> "Good Evening " + str + "!"; // Output: Good Morning Luis! System.out.println(morningGreeting.processName("Luis")); // Output: Good Evening Jessica! System.out.println(eveningGreeting.processName("Jessica")); }

Las variables morningGreetingy eveningGreeting, líneas 6 y 7 en el ejemplo anterior, hacen una referencia a la MyGreetinginterfaz y definen diferentes expresiones de saludo.

Al escribir una expresión lambda, también es posible especificar explícitamente el tipo de parámetro en la expresión de esta manera:

MyGreeting morningGreeting = (String str) -> "Good Morning " + str + "!"; MyGreeting eveningGreeting = (String str) -> "Good Evening " + str + "!";

Bloquear expresiones lambda

Hasta ahora, he cubierto muestras de lambdas de expresión única. Hay otro tipo de expresión que se usa cuando el código en el lado derecho del operador de flecha contiene más de una declaración conocida como bloque lambdas :

interface MyString { String myStringFunction(String str); } public static void main (String args[]) { // Block lambda to reverse string MyString reverseStr = (str) -> { String result = ""; for(int i = str.length()-1; i >= 0; i--) result += str.charAt(i); return result; }; // Output: omeD adbmaL System.out.println(reverseStr.myStringFunction("Lambda Demo")); }

Interfaces funcionales genéricas

Una expresión lambda no puede ser genérica. Pero la interfaz funcional asociada con una expresión lambda sí puede. Es posible escribir una interfaz genérica y manejar diferentes tipos de retorno como este:

interface MyGeneric { T compute(T t); } public static void main(String args[]){ // String version of MyGenericInteface MyGeneric reverse = (str) -> { String result = ""; for(int i = str.length()-1; i >= 0; i--) result += str.charAt(i); return result; }; // Integer version of MyGeneric MyGeneric factorial = (Integer n) -> { int result = 1; for(int i=1; i <= n; i++) result = i * result; return result; }; // Output: omeD adbmaL System.out.println(reverse.compute("Lambda Demo")); // Output: 120 System.out.println(factorial.compute(5)); }

Expresiones lambda como argumentos

Un uso común de lambdas es pasarlos como argumentos.

Se pueden usar en cualquier código que proporcione un tipo de destino. Encuentro esto emocionante, ya que me permite pasar código ejecutable como argumentos a métodos.

Para pasar expresiones lambda como parámetros, solo asegúrese de que el tipo de interfaz funcional sea compatible con el parámetro requerido.

interface MyString { String myStringFunction(String str); } public static String reverseStr(MyString reverse, String str){ return reverse.myStringFunction(str); } public static void main (String args[]) { // Block lambda to reverse string MyString reverse = (str) -> { String result = ""; for(int i = str.length()-1; i >= 0; i--) result += str.charAt(i); return result; }; // Output: omeD adbmaL System.out.println(reverseStr(reverse, "Lambda Demo")); }

Estos conceptos le brindarán una buena base para comenzar a trabajar con expresiones lambda. Eche un vistazo a su código y vea dónde puede aumentar el poder expresivo de Java.