¿Por qué usar expresiones lambda?
Echemos un vistazo a algunos ejemplos:
El primer ejemplo es ejecutar una tarea en un hilo separado, que generalmente implementamos de la siguiente manera:
Class Worker implementa Runnable {public void run () {for (int i = 0; i <100; i ++) dowork (); } ...} trabajador w = nuevo trabajador (); nuevo hilo (w) .Start ();El segundo ejemplo es un método de comparación de cadenas personalizado (por longitud de cadena), que generalmente se realiza:
Class LongitudComparator implementa comparador <String> {public int Compare (String First, String Second) {return Integer.Compare (first.length (), segundo.length ()); }} Arrays.sort (cadenas, new LongComparator ());En el tercer ejemplo, en Javafx, agregue una devolución de llamada a un botón:
Button.SetOnAction (new EventHandler <CtionEvent> () {public void Handle (ActionEvent Event) {System.out.println ("¡Gracias por hacer clic!");}});Estos ejemplos tienen una cosa en común, que es que primero definen un bloque de código, lo pasan a un objeto o método y luego lo ejecutan. Antes de las expresiones de Lambda, Java no permite el paso directo de los bloques de código, porque Java está orientado a objetos, por lo que se debe pasar un objeto para encapsular el bloque de código que se ejecuta en el objeto.
Sintaxis de expresión lambda
El Comparador de Longitud en el segundo ejemplo anterior se expresa como una expresión de Lambda:
(String First, String Second) -> Integer.Compare (first.length (), segundo.length ());
-> Antes es la lista de parámetros, seguido del cuerpo de la declaración de expresión;
Si el cuerpo de una declaración de expresión es más de una línea, el cuerpo de la declaración está escrito en {}, al igual que una función ordinaria:
(Cadena primero, cadena en segundo lugar) -> {if (first.length ()> segundo.length ()) {return 1; } else if (first.length () == Second.length ()) {return 0; } else {return -1; }};Si no hay parámetros, () todavía necesita ser traído con usted. Por ejemplo, el primer ejemplo anterior se puede expresar como:
() -> {for (int i = 0; i <1000; i ++) {dowork (); }}Si el tipo de parámetro se puede inferir automáticamente desde el contexto, puede omitir:
Comparador <String> comp = (primero, segundo) // lo mismo que (cadena primero, string segundo) -> integer.compare (first.length (), segundo.length ());
Si solo hay un parámetro y el tipo se puede inferir automáticamente, los soportes () también se pueden omitir:
// en lugar de (evento) -> o (Evento de ActionEvent) -> EventHandler <CtionEvent> Listener = Event -> System.out.println ("¡Gracias por hacer clic!");El tipo de valor de retorno de la expresión de lambda se infiere automáticamente, por lo que no es necesario especificar; En la expresión de Lambda, algunas ramas condicionales tienen valores de retorno, pero otras ramas no tienen valores de retorno, lo que no está permitido, como:
(x) -> {if (x> = 0) {return 1; }}Además, la diferencia entre la expresión lambda y la declaración lambda es que la expresión lambda no necesita escribir la palabra clave de retorno. El tiempo de ejecución de Java devolverá el resultado de la expresión como el valor de retorno, mientras que la declaración Lambda es una expresión escrita en {}, y la palabra clave de retorno debe usarse, por ejemplo:
// Expression lambdacomparator <String> comp1 = (Primero, segundo) -> integer.compare (first.length (), segundo.length ()); // instrucciones lambdacomparator <string> comp2 = (first, segundo) -> {return Integer.compare (first.length (), segundo.length ());};}; Interfaz funcional
Si una interfaz tiene solo un método abstracto, se llama
Interfaz funcional, como runnable, comparador, etc.
En cualquier lugar donde se necesite objeto de interfaz funcional, puede usar expresiones lambda:
Arrays.sort (palabras, (primero, segundo) -> integer.compare (first.length (), segundo.length ()));
Aquí, el segundo parámetro de sort () requiere un objeto comparador, y el comparador es
Interfaz funcional, por lo que puede pasar directamente en la expresión de lambda. Al llamar al método compare () del objeto, es ejecutar el cuerpo de la declaración en la expresión lambda;
Si la declaración de la expresión de Lambda lanza una excepción, el método abstracto correspondiente en la interfaz funcional debe lanzar la excepción, de lo contrario, es necesario detectar explícitamente la excepción en la expresión de Lambda:
Runnable r = ()-> {System.out.println ("-------"); intente {thread.sleep (10); } capt (interruptedException e) {// Catch Exception}}; Callable <String> C = ()-> {System.out.println ("----------"); Thread.sleep (10); devolver "";}; Referencia de método
Si los parámetros de la expresión de lambda se pasan como parámetros a un método y su efecto de ejecución es el mismo, la expresión de lambda puede expresarse utilizando la referencia del método, y los siguientes dos métodos son equivalentes:
(x) -> System.out.println (x) System.out :: println
Entre ellos, System.out :: Println se llama referencia de método.
La referencia del método viene principalmente en tres formas:
Para los dos primeros métodos, los parámetros de expresión de Lambda correspondientes y los parámetros del método son los mismos, como:
System.out :: println (x) -> System.out.println (x) Math :: Pow (x, y) -> Math.Pow (x, y)
Para el tercer método, en el cuerpo correspondiente de la declaración de expresión lambda, el primer parámetro se usa como objeto, se llama el método y se usan otros parámetros como parámetros del método, como:
String :: CompareToignorecase (S1, S2) -> S1.comparetoignorecase (S2) 1.5 Referencia de constructor
La referencia del constructor es similar a la referencia del método, pero es un método especial: nuevo. El constructor específico está determinado por el entorno de contexto, como:
List <String> etiquetas = ...; stream <boton> stream = labels.stream (). Map (botón :: nuevo);
Botón :: nuevo es equivalente a (x) -> botón (x), por lo que el constructor llamado es: botón (x);
Además de crear un solo objeto, también puede crear una matriz de objetos, como los siguientes dos equivalentes:
int [] :: nuevo (x) -> nuevo int [x]
Alcance variable
Variables de captura de expresiones LAMBD disponibles en el alcance actual, como:
public void RepetMessage (texto de cadena, int count) {runnable r = () -> {for (int i = 0; i <count; i ++) {System.out.println (text); Thread.yield (); }}; nuevo hilo (r) .Start ();}Pero estas variables deben ser inmutables, ¿por qué? Vea el siguiente ejemplo:
int coincidentes = 0; para (ruta p: archivos) nuevo hilo (() -> {if (p tiene alguna propiedad) coincidencias ++;}). start (); // ilegal para mutar partidosDebido a que las variables mutables no son seguras de hilo en las expresiones lambda, esto es consistente con los requisitos de las clases internas, y solo las variables finales definidas externamente pueden referenciarse en clases internas;
El alcance de la expresión de lambda es el mismo que el del bloque de código anidado, por lo que el nombre del parámetro o el nombre de la variable en la expresión LAMBD no puede entrar en conflicto con las variables locales, como:
Ruta primero = paths.get ("/usr/bin"); comparador <string> comp = (primero, segundo) -> integer.compare (first.length (), segundo.length ()); // Error: variable primero ya definidoSi se hace referencia a esta variable en una expresión de lambda, la referencia es esta variable del método que crea la expresión de lambda, como:
Public Class Application () {public void dowork () {runnable runner = () -> {...; System.out.println (this.ToString ()); ...}; }} Así que aquí este.toString () llama a toString () del objeto de aplicación, no se ejecuta
objeto.
Método predeterminado
Solo puede haber métodos abstractos en la interfaz. Si se agrega un nuevo método a una interfaz existente, todas las clases de implementación de la interfaz deben implementar este método.
Java 8 presenta el concepto de método predeterminado y agrega un método predeterminado a la interfaz, que no destruirá las reglas de interfaz existentes. La clase de implementación de la interfaz puede elegir anular o heredar directamente el método predeterminado, como:
interfaz persona {long getId (); cadena predeterminada getName () {return "John Q. public"; }}Java permite la herencia múltiple. Cómo lidiar con este conflicto si los métodos definidos en la clase principal de una clase son exactamente los mismos que los métodos predeterminados definidos en la interfaz, o las dos interfaces de una clase son exactamente las mismas, ¿cómo lidiar con este conflicto? Las reglas de procesamiento son las siguientes:
Si el método entra en conflicto entre la clase principal y la interfaz: los métodos en la clase principal prevalecerán, y los métodos en la interfaz se ignorarán;
Si el método predeterminado en las dos interfaces conflictos, debe anular el método para resolver el conflicto;
Método estático
Antes de Java 8, solo se pueden definir variables estáticas en la interfaz. A partir de Java 8, se pueden agregar métodos estáticos a la interfaz, como
La interfaz de comparación ha agregado una serie de métodos estáticos para compararxxx, como:
Public static <t> Comparator <T> ComparingInt (tointFunction <? Super t> keyExtractor) {Objects.RquirenonNull (KeyExtractor); return (comparador <t> y serializable) (c1, c2) -> integer.compare (keyExtractor.applyAsInt (C1), keyExtractor.applyAsInt (C2));}Usando este método estático, los siguientes dos métodos también son equivalentes:
1.
Arrays.sort (ciudades, (primer, segundo) -> integer.compare (first.length (), segundo.length ()));
2.
Arrays.sort (ciudad, comparador.comparingint (string :: longitud));
Por lo tanto, cuando diseñamos nuestras propias interfaces en el futuro, ya no necesitamos definir clases de herramientas separadas (como colecciones/colección).
Simplemente use el método estático en la interfaz.
Clase interna anónima
En el mundo de Java, las clases internas anónimas pueden implementar operaciones que solo se pueden realizar una vez en una aplicación. Por ejemplo, en una aplicación de Android, se maneja un evento de clic de botón. No necesita escribir una clase separada para manejar un evento de clic, puede hacerlo con una clase interna anónima:
Botón botón = (botón) findViewById (R.id.Button1); Button.SetOnClickListener (new onClickListener () {@Override public void onClick (View) {toast.maketext (mainActivity.this, "botón hecho", toast.length_short) .show ();}}); Lambda Ejemplo 1. Lambda ejecutable Veamos varios ejemplos. Aquí hay un ejemplo de Runnable: public void runnabletest () {System.out.println ("=== runnabletest ==="); // un anónimo runnable runnable r1 = new runnable () {@Override public void run () {System.out.println ("¡Hola World One!"); }}; // lambda runnable runnable r2 = () -> system.out.println ("¡Hola Mundo dos!"); // ejecutar dos funciones de ejecución r1.run (); r2.run (); } public void runnabletest () {System.out.println ("=== runnabletest ==="); // un anónimo runnable runnable r1 = new runnable () {@Override public void run () {System.out.println ("¡Hola World One!"); }}; // lambda runnable runnable r2 = () -> system.out.println ("¡Hola Mundo dos!"); // ejecutar dos funciones de ejecución r1.run (); r2.run (); } Ni la implementación ni el valor de retorno se devuelven. Expresiones Lambda Runnable Use bloques de código para simplificar el código de cinco elementos en una declaración. Persona de clase pública {cadena privada dadaName; apellido de cadena privada; edad privada int; género privado de género; correo electrónico de cadena privada; Teléfono de cadena privada; dirección de cadena privada;} Persona de clase pública {cadena privada dadaName; apellido de cadena privada; edad privada int; género privado de género; correo electrónico de cadena privada; Teléfono de cadena privada; dirección de cadena privada;} La siguiente es cómo implementar la interfaz de comparación utilizando clases internas anónimas y expresiones Lambda: Public Class Comparatortest {public static void main (string [] args) {list <Oll> personList = persona.createShortList (); // Use la clase interior para implementar colecciones de clasificación.sort (PersonList, New Comparator <Oll> () {public int Compare (Person P1, Person P2) {return P1.getSurname (). Compareto (p2.getSurname ());}}); System.out.println ("=== Surname Surname ==="); para (persona P: Personlist) {P.PrintName (); } // Implementación utilizando la expresión de Lambda // ascendente System.out.println ("=== Surname Surname ==="); Colección.sort (Personlist, (Person P1, Persona P2) -> P1.getSurname (). Compareto (p2.getSurname ())); para (persona P: Personlist) {P.PrintName (); } // DESC Subsecuente System.out.println ("=== Sellado de descremisión de descremado ==="); Colección.sort (Personlist, (P1, P2) -> P2.getSurname (). Compareto (p1.getSurname ())); para (persona P: Personlist) {P.PrintName (); }}} Public Class Comparatortest {public static void main (string [] args) {list <Oll> personList = persona.createShortList (); // Use la clase interior para implementar colecciones de clasificación.sort (PersonList, New Comparator <Oll> () {public int Compare (Person P1, Person P2) {return P1.getSurname (). Compareto (p2.getSurname ());}}); System.out.println ("=== Surname Surname ==="); para (persona P: Personlist) {P.PrintName (); } // Implementación utilizando la expresión de Lambda // ascendente System.out.println ("=== Surname Surname ==="); Colección.sort (Personlist, (Person P1, Persona P2) -> P1.getSurname (). Compareto (p2.getSurname ())); para (persona P: Personlist) {P.PrintName (); } // DESC Subsecuente System.out.println ("=== Sellado de descremisión de descremado ==="); Colección.sort (Personlist, (P1, P2) -> P2.getSurname (). Compareto (p1.getSurname ())); para (persona P: Personlist) {P.PrintName (); }}} Puede ver que las clases internas anónimas se pueden implementar a través de expresiones Lambda. Tenga en cuenta que la primera expresión de lambda define el tipo de parámetro como persona; La segunda expresión de Lambda omite la definición de tipo. Lambda Expressions admite la eliminación del tipo de tipo, y si el tipo requerido se puede deducir a través del contexto, se puede omitir la definición de tipo. Aquí, dado que usamos expresiones lambda en un comparador que usa definiciones genéricas, el compilador puede deducir estos dos parámetros como persona.