JavaRush /Blog Java /Random-ES /Expresiones lambda con ejemplos.

Expresiones lambda con ejemplos.

Publicado en el grupo Random-ES
Inicialmente, Java es un lenguaje completamente orientado a objetos. Con excepción de los tipos primitivos, todo en Java es un objeto. Incluso las matrices son objetos. Las instancias de cada clase son objetos. No existe una sola posibilidad de definir una función por separado (fuera de una clase - aprox. transl. ). Y no hay forma de pasar un método como argumento o devolver el cuerpo de un método como resultado de otro método. Es así. Pero esto fue antes de Java 8. Expresiones lambda con ejemplos - 1Desde los días del viejo Swing, era necesario escribir clases anónimas cuando era necesario pasar alguna funcionalidad a algún método. Por ejemplo, así es como se veía agregar un controlador de eventos:
someObject.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {

                //Event listener implementation goes here...

            }
        });
Aquí queremos agregar algo de código al detector de eventos del mouse. Definimos una clase anónima MouseAdaptere inmediatamente creamos un objeto a partir de ella. De esta manera, hemos pasado funcionalidad adicional al archivo addMouseListener. En resumen, no es fácil pasar un método simple (funcionalidad) en Java a través de argumentos. Esta limitación obligó a los desarrolladores de Java 8 a agregar una característica como las expresiones Lambda a la especificación del lenguaje.

¿Por qué Java necesita expresiones Lambda?

Desde el principio, el lenguaje Java no ha evolucionado mucho, excepto en cosas como anotaciones, genéricos, etc. En primer lugar, Java siempre ha estado orientado a objetos. Después de trabajar con lenguajes funcionales como JavaScript, se puede comprender cómo Java está estrictamente orientado a objetos y fuertemente tipado. Las funciones no son necesarias en Java. Por sí solos, no se pueden encontrar en el mundo Java. En los lenguajes de programación funcionales, las funciones pasan a primer plano. Existen por sí solos. Puede asignarlos a variables y pasarlos a través de argumentos a otras funciones. JavaScript es uno de los mejores ejemplos de lenguajes de programación funcionales. Puede encontrar buenos artículos en Internet que detallan los beneficios de JavaScript como lenguaje funcional. Los lenguajes funcionales tienen herramientas poderosas como Closure, que brindan una serie de ventajas sobre los métodos tradicionales de escritura de aplicaciones. Un cierre es una función con un entorno adjunto: una tabla que almacena referencias a todas las variables no locales de la función. En Java, los cierres se pueden simular mediante expresiones Lambda. Por supuesto, existen diferencias entre cierres y expresiones lambda, y no pequeñas, pero las expresiones lambda son una buena alternativa a los cierres. En su blog sarcástico y divertido, Steve Yegge describe cómo el mundo de Java está estrictamente ligado a los sustantivos (entidades, objetos - aprox. traducción ). Si no has leído su blog, te lo recomiendo. Describe de una manera divertida e interesante la razón exacta por la que se agregaron expresiones Lambda a Java. Las expresiones Lambda aportan a Java una funcionalidad que ha estado ausente durante tanto tiempo. Las expresiones lambda aportan funcionalidad al lenguaje al igual que los objetos. Si bien esto no es 100% cierto, puede ver que las expresiones Lambda, aunque no son cierres, brindan capacidades similares. En un lenguaje funcional, las expresiones lambda son funciones; pero en Java, las expresiones lambda están representadas por objetos y deben asociarse con un tipo de objeto específico llamado interfaz funcional. A continuación veremos qué es. El artículo de Mario Fusco "Por qué necesitamos la expresión Lambda en Java" describe en detalle por qué todos los lenguajes modernos necesitan capacidades de cierre.

Introducción a las expresiones Lambda

Las expresiones lambda son funciones anónimas (puede que no sea una definición 100% correcta para Java, pero aporta cierta claridad). En pocas palabras, este es un método sin declaración, es decir. sin modificadores de acceso, devolviendo valor y nombre. En resumen, te permiten escribir un método y utilizarlo inmediatamente. Es especialmente útil en el caso de una llamada a un método única, porque Reduce el tiempo que lleva declarar y escribir un método sin tener que crear una clase. Las expresiones Lambda en Java suelen tener la siguiente sintaxis (аргументы) -> (тело). Por ejemplo:
(арг1, арг2...) -> { тело }

(тип1 арг1, тип2 арг2...) -> { тело }
A continuación se muestran algunos ejemplos de expresiones Lambda reales:
(int a, int b) -> {  return a + b; }

() -> System.out.println("Hello World");

(String s) -> { System.out.println(s); }

() -> 42

() -> { return 3.1415 };

Estructura de las expresiones Lambda

Estudiemos la estructura de las expresiones lambda:
  • Las expresiones lambda pueden tener 0 o más parámetros de entrada.
  • El tipo de parámetros se puede especificar explícitamente o se puede obtener del contexto. Por ejemplo ( int a) se puede escribir así ( a)
  • Los parámetros están entre paréntesis y separados por comas. Por ejemplo ( a, b) o ( int a, int b) o ( String a, int b, float c)
  • Si no hay parámetros, deberá utilizar paréntesis vacíos. Por ejemplo() -> 42
  • Cuando solo hay un parámetro, si el tipo no se especifica explícitamente, se pueden omitir los paréntesis. Ejemplo:a -> return a*a
  • El cuerpo de una expresión Lambda puede contener 0 o más expresiones.
  • Si el cuerpo consta de una sola declaración, no puede estar entre llaves y el valor de retorno puede especificarse sin la palabra clave return.
  • De lo contrario, las llaves son obligatorias (bloque de código) y el valor de retorno debe especificarse al final usando una palabra clave return(de lo contrario, el tipo de retorno será void).

¿Qué es una interfaz funcional?

En Java, las interfaces Marker son interfaces sin declarar métodos o campos. En otras palabras, las interfaces token son interfaces vacías. De manera similar, las interfaces funcionales son interfaces con un solo método abstracto declarado en ellas. java.lang.Runnablees un ejemplo de una interfaz funcional. Sólo declara un método void run(). También hay una interfaz ActionListener, también funcional. Anteriormente, teníamos que usar clases anónimas para crear objetos que implementaran una interfaz funcional. Con las expresiones Lambda, todo se ha vuelto más sencillo. Cada expresión lambda se puede vincular implícitamente a alguna interfaz funcional. Por ejemplo, puede crear una referencia a Runnableuna interfaz, como se muestra en el siguiente ejemplo:
Runnable r = () -> System.out.println("hello world");
Este tipo de conversión siempre se realiza implícitamente cuando no especificamos una interfaz funcional:
new Thread(
    () -> System.out.println("hello world")
).start();
En el ejemplo anterior, el compilador crea automáticamente una expresión lambda como una implementación Runnablede la interfaz desde el constructor de la clase Thread:. public Thread(Runnable r) { }A continuación se muestran algunos ejemplos de expresiones lambda y sus correspondientes interfaces funcionales:
Consumer<Integer> c = (int x) -> { System.out.println(x) };

BiConsumer<Integer, String> b = (Integer x, String y) -> System.out.println(x + " : " + y);

Predicate<String> p = (String s) -> { s == null };
La anotación @FunctionalInterfaceagregada en Java 8 de acuerdo con la Especificación del lenguaje Java verifica si la interfaz declarada es funcional. Además, Java 8 incluye una serie de interfaces funcionales listas para usar con expresiones Lambda. @FunctionalInterfacearrojará un error de compilación si la interfaz declarada no es funcional. El siguiente es un ejemplo de definición de una interfaz funcional:
@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();

}
Como sugiere la definición, una interfaz funcional sólo puede tener un método abstracto. Si intenta agregar otro método abstracto, obtendrá un error de compilación. Ejemplo:
@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();

    public void doSomeMoreWork();

}
Error
Unexpected @FunctionalInterface annotation
    @FunctionalInterface ^ WorkerInterface is not a functional interface multiple
    non-overriding abstract methods found in interface WorkerInterface 1 error
После определения функционального интерфейса, мы можем его использовать и получать все преимущества Lambda-выражений. Пример:// definiendo una interfaz funcional
@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();

}
public class WorkerInterfaceTest {

    public static void execute(WorkerInterface worker) {
        worker.doSomeWork();
    }

    public static void main(String [] args) {

      // llamando al método doSomeWork a través de una clase anónima
      // (clásico)
      execute(new WorkerInterface() {
            @Override
            public void doSomeWork() {
               System.out.println("Trabajador llamado a través de una clase anónima");
            }
        });

      // llamando al método doSomeWork a través de expresiones Lambda
      // (Java 8 nuevo)
      execute( () -> System.out.println("Trabajador llamado a través de Lambda") );
    }

}
Conclusión:
Worker вызван через анонимный класс
Worker вызван через Lambda
Aquí definimos nuestra propia interfaz funcional y utilizamos una expresión lambda. El método execute()es capaz de aceptar expresiones lambda como argumento.

Ejemplos de expresiones Lambda

La mejor manera de comprender las expresiones Lambda es observar algunos ejemplos: Una secuencia Threadse puede inicializar de dos maneras:
// Vieja forma:
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello from thread");
    }
}).start();
// Nueva manera:
new Thread(
    () -> System.out.println("Hello from thread")
).start();
La gestión de eventos en Java 8 también se puede realizar mediante expresiones Lambda. Las siguientes son dos formas de agregar un controlador de eventos ActionListenera un componente de la interfaz de usuario:
// Vieja forma:
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Botón presionado. ¡A la antigua!");
    }
});
// Nueva manera:
button.addActionListener( (e) -> {
        System.out.println("Botón presionado. ¡Lambda!");
});
Un ejemplo sencillo de cómo mostrar todos los elementos de una matriz determinada. Tenga en cuenta que hay más de una forma de utilizar una expresión lambda. A continuación creamos una expresión lambda de la forma habitual usando la sintaxis de flecha, y también usamos el operador de dos puntos dobles (::), que en Java 8 convierte un método regular en una expresión lambda:
// Vieja forma:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for(Integer n: list) {
    System.out.println(n);
}
// Nueva manera:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> System.out.println(n));
// Nueva forma de usar el operador de dos puntos ::
list.forEach(System.out::println);
En el siguiente ejemplo, utilizamos una interfaz funcional Predicatepara crear una prueba e imprimir elementos que pasan esa prueba. De esta manera puedes poner lógica en expresiones lambda y hacer cosas basadas en ella.
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class Main {

    public static void main(String [] a)  {

        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);

        System.out.print("Emite todos los números: ");
        evaluate(list, (n)->true);

        System.out.print("No da salida a ningún número:");
        evaluate(list, (n)->false);

        System.out.print("Salida de números pares:");
        evaluate(list, (n)-> n%2 == 0 );

        System.out.print("Salida de números impares:");
        evaluate(list, (n)-> n%2 == 1 );

        System.out.print("Números de salida mayores que 5:");
        evaluate(list, (n)-> n > 5 );

    }

    public static void evaluate(List<Integer> list, Predicate<Integer> predicate) {
        for(Integer n: list)  {
            if(predicate.test(n)) {
                System.out.print(n + " ");
            }
        }
        System.out.println();
    }

}
Conclusión:
Выводит все числа: 1 2 3 4 5 6 7
Не выводит ни одного числа:
Вывод четных чисел: 2 4 6
Вывод нечетных чисел: 1 3 5 7
Вывод чисел больше 5: 6 7
Al jugar con expresiones Lambda, puede mostrar el cuadrado de cada elemento de la lista. Observe que estamos usando el método stream()para convertir una lista normal en una secuencia. Java 8 proporciona una clase increíble Stream( java.util.stream.Stream). Contiene toneladas de métodos útiles con los que puedes usar expresiones lambda. Pasamos una expresión lambda x -> x*xal método map(), que la aplica a todos los elementos de la secuencia. Después de lo cual usamos forEachpara imprimir todos los elementos de la lista.
// Vieja forma:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
for(Integer n : list) {
    int x = n * n;
    System.out.println(x);
}
// Nueva manera:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
list.stream().map((x) -> x*x).forEach(System.out::println);
Dada una lista, debes imprimir la suma de los cuadrados de todos los elementos de la lista. Las expresiones Lambda le permiten lograr esto escribiendo solo una línea de código. Este ejemplo utiliza el método de convolución (reducción) reduce(). Usamos un método map()para elevar al cuadrado cada elemento y luego usamos un método reduce()para colapsar todos los elementos en un solo número.
// Vieja forma:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = 0;
for(Integer n : list) {
    int x = n * n;
    sum = sum + x;
}
System.out.println(sum);
// Nueva manera:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get();
System.out.println(sum);

La diferencia entre expresiones Lambda y clases anónimas

La principal diferencia es el uso de la palabra clave this. Para clases anónimas, la palabra clave '' thisdenota un objeto de la clase anónima, mientras que en una expresión lambda, ' this' denota un objeto de la clase en la que se usa la expresión lambda. Otra diferencia es la forma en que están compilados. Java compila expresiones lambda y las convierte en privatemétodos de clase. Esto utiliza la instrucción invokedynamic , introducida en Java 7 para el enlace de métodos dinámicos. Tal Weiss describió en su blog cómo Java compila expresiones lambda en código de bytes.

Conclusión

Mark Reinhold (arquitecto jefe de Oracle) calificó las expresiones Lambda como el cambio más significativo en el modelo de programación que jamás haya ocurrido, incluso más significativo que los genéricos. Debe tener razón, porque... brindan a los programadores de Java las capacidades del lenguaje de programación funcional que todos estaban esperando. Junto con innovaciones como los métodos de extensión virtual, las expresiones Lambda le permiten escribir código de muy alta calidad. Espero que este artículo te haya dado un vistazo bajo el capó de Java 8. Buena suerte :)
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION