Descripción de la sintaxis
Una expresión de lambda consiste en las siguientes partes:
1. Lista separada por comas de parámetros formales entre paréntesis. El método Checkperson.Test contiene un parámetro P, que representa una instancia de la clase de persona. Nota: Los tipos de parámetros en las expresiones Lambda se pueden omitir; Además, si solo hay un parámetro, los soportes también se pueden omitir. Por ejemplo, el código mencionado en la sección anterior:
P -> p.getGender () == Person.sex.male && p.getage ()> = 18 && p.getage () <= 25
2. Símbolo de flecha: ->. Utilizado para separar parámetros y cuerpos de funciones.
3. Función cuerpo. Que consiste en una expresión o bloque de código. En el ejemplo en la sección anterior, se usó esta expresión:
p.getGender () == Person.sex.male && p.getage ()> = 18 && p.getage () <= 25
Si está utilizando una expresión, el tiempo de ejecución de Java calculará y devolverá el valor de la expresión. Además, también puede optar por usar la instrucción de retorno en el bloque de código:
p -> {return p.getGender () == Person.sex.male && p.getage ()> = 18 && p.getage () <= 25; }Sin embargo, la declaración de retorno no es una expresión. En las expresiones de Lambda, la declaración debe estar encerrada en aparatos ortopédicos rizados, pero no es necesario encerrar las declaraciones en aparatos ortopédicos rizados cuando solo llame a un método con un valor de retorno vacío, por lo que el siguiente método de escritura también es correcto:
Correo electrónico -> System.out.println (correo electrónico)
Hay muchas similitudes en la Declaración de Expresiones y Métodos de Lambda. Por lo tanto, las expresiones de Lambda también pueden considerarse como métodos anónimos, es decir, métodos sin una definición de nombre.
Las expresiones Lambda mencionadas anteriormente son todas las expresiones que solo usan un parámetro como parámetro formal. La siguiente clase de ejemplo, Caulator, demuestra cómo usar múltiples parámetros como parámetros formales:
paquete com.zhyea.zytools; calculadora de clase pública {interfaz Integermath {int Operation (int a, int b); } public int OperationBinary (int a, int b, Integermath op) {return op.operation (a, b); } public static void main (string ... args) {calculator myApp = new Calculator (); Integermath adición = (a, b) -> a + b; System.out.println ("40 + 2 =" + myApp.OpererBinary (40, 2, adición)); System.out.println ("20 - 10 =" + myApp.OpererBinary (20, 10, subtracción)); }}El método OperatingBinary en el código utiliza dos parámetros enteros para realizar operaciones aritméticas. La operación aritmética aquí es en sí misma un ejemplo de la interfaz Integermath. En el programa anterior, dos operaciones aritméticas se definen utilizando expresiones lambda: adición y resta. Ejecutar el programa imprimirá el siguiente contenido:
40 + 2 = 4220 - 10 = 10
Acceder a variables locales de clases externas
Similar a las clases locales o clases anónimas, las expresiones de Lambda también pueden acceder a variables locales de clases externas. La diferencia es que no hay necesidad de considerar problemas como anular al usar expresiones lambda. La expresión de Lambda es solo un concepto léxico, lo que significa que no necesita heredar ningún nombre de la superclase, ni introduce nuevos ámbitos. Es decir, la declaración en una expresión de lambda tiene el mismo significado que la declaración en su entorno externo. Esto se demuestra en el siguiente ejemplo:
paquete com.zhyea.zytools; import java.util.function.consumer; public class lambdascopetest {public int x = 0; clase FirstLevel {public int x = 1; Void MethodInfirstLevel (int x) {// La siguiente declaración hará que el compilador informe un error "Las variables locales a las que se hace referencia desde una expresión de lambda debe ser final o efectivamente final" // x = 99; Consumer <Integer> myconsumer = (y) -> {System.out.println ("x =" + x); // Declaración un sistema.out.println ("y =" + y); System.out.println ("this.x =" + this.x); System.out.println ("lambdascopetest.this.x =" + lambdascopetest.this.x); }; myconsumer.accept (x); }} public static void main (string ... args) {lambdascopetest st = new lambDascoPetest (); Lambdascopetest.Firstlevel fl = St.new FirstLevel (); fl.methodinfirstlevel (23); }}Este código emitirá lo siguiente:
x = 23y = 23this.x = 1Lambdascopetest.This.x = 0
Si reemplaza el parámetro Y en el miconsumidor de expresión lambda en el ejemplo con x, el compilador informará un error:
Consumer <Integer> myconsumer = (x) -> {// ....};El mensaje de error del compilador es: "La variable X ya está definida en el método MethodInfirstLevel (int)", lo que significa que la variable X se ha definido en el método MethodInfirstLevel. Se informa un error porque la expresión de Lambda no introduce un nuevo alcance. Por lo tanto, puede acceder directamente a los campos de dominio, métodos y parámetros formales de la clase externa en la expresión de Lambda. En este ejemplo, el miconsumidor de expresión lambda accede directamente al parámetro X del método MethodInfirstLevel. Al acceder a los miembros de clases externas, esta palabra clave también se usa directamente. En este ejemplo, esto.x se refiere a FirstLevel.x.
Sin embargo, como las clases locales o anónimas, las expresiones de Lambda solo pueden acceder a variables locales o miembros externos declarados como finales (o equivalentes a finales). Por ejemplo, eliminamos el comentario antes de "x = 99" en el método del código de muestra en el método de nivel:
// La siguiente declaración hará que el compilador informe un error "Las variables locales a las que se hace referencia desde una expresión de lambda debe ser final o efectivamente final" x = 99; Consumer <Integer> myconsumer = (y) -> {System.out.println ("x =" + x); // Declaración un sistema.out.println ("y =" + y); System.out.println ("this.x =" + this.x); System.out.println ("lambdascopetest.this.x =" + lambdascopetest.this.x); };Debido a que el valor del parámetro X se modifica en esta declaración, el parámetro X de MethodInfirstLevel ya no puede considerarse como final. Por lo tanto, el compilador Java informará un error como "las variables locales a las que se hace referencia desde una expresión de Lambda debe ser final o efectivamente final" donde la expresión de Lambda accede a la variable local x.
Tipo objetivo
¿Cómo determinar el tipo de expresión de lambda? Echemos un vistazo al código para filtrar el personal militar de la edad apropiada:
P -> p.getGender () == Person.sex.male && p.getage ()> = 18 && p.getage () <= 25
Este código se ha utilizado en dos lugares:
Public static void printPersons (lista <oman> lista, probador de verificación) - Solución 3
public void printPersonsonswithPredicate (Lista <Oll> Roster, Predicate <Oll> Tester) - Plan VI
Al llamar al método PrintPersons, este método espera un parámetro de tipo de verificación. En este momento, la expresión anterior es una expresión de tipo de verificación. Al llamar al método printPersonsonswithPredicate, se espera un parámetro de predicado de tipo <Oll>. En este momento, la misma expresión es de tipo predicado <OlPER>. Como este, el tipo determinado por el tipo esperado por el método se llama tipo de destino (en realidad, creo que la inferencia de tipo en Scala es más apropiada aquí). El compilador Java determina el tipo de expresión de Lambda a través del contexto del tipo de destino o la posición al descubrir la expresión de lambda. Esto significa que las expresiones lambda solo se pueden usar donde el compilador Java puede inferir el tipo:
Tipo de destino y parámetros de método
Para los parámetros del método, el compilador Java también debe confiar en dos características del lenguaje para determinar el tipo de destino: análisis de sobrecarga e inferencia de parámetros de tipo.
Eche un vistazo a las siguientes dos interfaces funcionales (java.lang.runnable y java.util.concurrent.callable <v>):
interfaz pública runnable {void run (); } interfaz pública llamable <v> {v call (); }El método runnable.run () no devuelve un valor, mientras que el método Callable.call () lo tiene.
Supongamos que sobrecargamos el método de invocación como el siguiente:
Void Invoke (runnable r) {r.run (); } <t> t invoke (llamable <t> c) {return c.call (); }Entonces, ¿qué método se llamará en la siguiente declaración:
String s = invoke(() -> "done");
Se llama a Invoke (Callable <T>) porque este método tiene un valor de retorno, mientras que Invoke (Runnable <T>) no devuelve el valor. En este caso, el tipo de expresión lambda (() -> "hecho") se puede llamar <t>.
Publicación por entregas
Si el tipo objetivo de una expresión lambda y los tipos de parámetros que llama son serializables, entonces la expresión de lambda también es serializable. Sin embargo, al igual que las clases internas, la serialización de las expresiones lambda no se recomienda fuertemente.