El Javase8 publicado en 2013 incluirá un plan llamado Lambda Project, que se describe en el Draft JSR-335 en junio de este año.
JSR-335 introdujo el cierre en Java. El cierre existe en muchos idiomas populares, como C ++, C#. El cierre nos permite crear un puntero de funciones y pasarlos como parámetro. En este artículo, analizaremos aproximadamente las características de Java8 e introduciremos expresiones Lambda. Y intentaré poner algunos programas de muestra para explicar algunos conceptos y gramática.
El lenguaje de programación Java nos proporciona el concepto de la interfaz, y el método abstracto se puede definir en la interfaz. La interfaz define la API y espera que los usuarios o proveedores implementen estos métodos. Muchas veces, no creamos clases de implementación independientes para algunas interfaces.
El uso anónimo es ampliamente utilizado. La escena más común utilizada en la clase interna anónima es el procesador de eventos. En segundo lugar, las clases internas anónimas a menudo se usan en programas múltiples.
Al igual que estamos discutiendo, una clase anónima es la implementación de una interfaz dada de un ninja. Por lo general, pasamos el objeto de esta clase de implementación como parámetro a un método, y luego este método llamará al método de clase de implementación a la clase de implementación internamente. Por lo tanto, esta interfaz se llama interfaz de devolución de llamada.
Aunque las categorías anónimas se usan en todas partes, todavía tienen muchos problemas. El primer problema principal es la complejidad. Estas clases hacen que los niveles del código parezcan desordenados y complicados, también conocidos como VERTICAL PRIVEM. En segundo lugar, no pueden acceder a los miembros no finales de la clase de envasado. La palabra clave de esto se volverá muy confusa. Si una clase anónima tiene el mismo nombre de miembro que su clase de empaque, las variables internas cubrirán las variables de los miembros externas, los miembros externos serán invisibles dentro de la categoría anónima. Porque esta palabra clave es digna de objeto anónimo en sí mismo en lugar de su objeto de encapsulación.
Public void AnonyMousample () {String Non -FinalVariable = "Nonformal Exmple"; Ejecutar la variable ";/ La línea a continuación proporciona un error de compilación. //System.out.println ("-> "" " + no FinalVariable); println ("->" + this.variable);}}). La salida es:
-> Varial de método de ejecución-> miembro de clase ejecutable
Este ejemplo explica el problema que mencioné anteriormente, y la expresión de Lambda casi resuelve todos los problemas causados por clases internas anónimas. Antes de explorar más a fondo la expresión de Lambda, echemos un vistazo a las interfaces funcionales.
Interfaces funcionales
Las interfaces funcionales son una interfaz con un solo método, que representa este contrato de método.
Solo uno en la definición anterior no es tan simple. No entiendo este párrafo.
El siguiente ejemplo muestra claramente cómo comprender el concepto de interfaces funcionales.
interfaz runnable {void run ();} // funcionalInterface foo {boolean iguales (objeto obj);} // no funcional; Funcional; no funcional; misma firmaLa mayoría de las interfaces de devolución de llamada son interfaces funcionales. Por ejemplo, ejecutable, llamable, comparador, etc. Anteriormente se llamaba SAM (método de resumen único)
Expresión de lambda
Como dijimos, un problema principal de la categoría anónima es que el nivel del código se ve desordenado, es decir, la expresión vertical. La expresión de Lambda parece un método. Tienen una lista de parámetros formal y un bloqueo de estos parámetros.
(String S) -> S.Lengh;
El ejemplo anterior significa que la primera expresión recibe una variable de cadena como parámetro y luego devuelve la longitud de la cadena. El segundo sin parámetros y return 43. Finalmente, el tercero aceptó dos enteros X e Y, y regresó a la paz.
Después de leer muchas palabras, finalmente, puedo dar un ejemplo de la primera expresión de lambda.
Clase pública FirstLambdaExpression {public String variable = "Variable de nivel de clase"; String Non -FinalVariable = "Esta es una variable no final"; ; La salida es:
-> Método Variable local-> Variable de nivel de clase
Puede comparar la diferencia entre usar expresiones lambda y clase interna anónima. Podemos decir claramente que escribir categorías anónimas utilizando la expresión de Lambda resuelve el problema de la visibilidad variable. Puede echar un vistazo a las anotaciones en el código.
La sintaxis de expresión de lambda general incluye una lista de parámetros, la palabra clave de flecha "->" es el cuerpo principal. El sujeto puede ser una expresión (declaración de una línea) o una oración múltiple. Si es una expresión, se calculará y se devolverá. Romper y continuar solo se puede usar dentro del ciclo.
Por qué elegir esta forma gramatical especial, porque actualmente este estilo suele estar en C# y Scala, que también es una escritura general de la expresión de Lambda. Este diseño de gramática básicamente resuelve la complejidad del tipo anónimo. Pero al mismo tiempo, también es muy flexible. El resultado de la expresión es como su propio valor de retorno. Esta flexibilidad puede mantener el código simple.
La expresión de lambda se usa como anónima, por lo que se pueden usar de manera flexible en otros módulos u otras expresiones lambda (expresiones de lambda anidadas).
// La exposición de Lambda está encerrada con el bloque de parámetros de métodos. (() -> {System.out.println ("Ejecutando en un hilo diferente");});
Si observa más de cerca la expresión de Lambda, verá que el tipo de interfaz de destino no es parte de una expresión. El compilador ayuda a inferir el tipo y el entorno circundante de la expresión de lambda.
La expresión de Lambda debe tener un tipo objetivo, y pueden adaptarse a cualquier posible tipo de objetivo. Cuando el tipo de destino es una interfaz, se deben cumplir las siguientes condiciones para compilar correctamente:
Debido a que el compilador puede conocer el tipo de parámetro y el número a través de la declaración de tipo de destino, en la expresión de Lambda, se puede omitir la declaración de tipo de parámetro.
Comparador C = (S1, S2) -> S1.com PARETOIGNORECASE (S2);
Además, si el método declarado en el tipo de destino solo recibe solo un parámetro (muchas veces este es el caso), entonces los pequeños soportes del parámetro también se pueden escribir, por ejemplo:
ActionListenr Listenr = Event-> Event.getWhen ();
Se produce una pregunta muy obvia, ¿por qué Lambda expresa no un nombre de método especificado?
La respuesta es: la expresión de lambda solo se puede usar para la interfaz funcional, mientras que la interfaz funcional tiene solo un método.
Cuando determinamos una interfaz funcional para crear expresiones lambda, el compilador puede percibir la firma de la interfaz funcional la ley china y verificar si la expresión dada coincide.
Esta gramática flexible nos ayuda a evitar usar el privem vertical anónimo, y no traerá privem horizontal (oración muy larga de una vía).
La gramática de expresión de lambda está relacionada con el contexto, pero estas no son la primera vez. Los operadores de diamantes agregados por Java SE 7 también tienen este concepto, que se infiere por contexto.
void invoke (runnable r) {r.run ()} void Future Invoke (Callable r) {return c.c.c.)} // arriba hay dos métodos en la interfaz de la interfaz s = invoke (() -> "do"); / ¿A qué invoque se llamará?
La respuesta a la pregunta anterior es llamar al método para recibir el parámetro invocable. En este caso, el compilador se resolverá a través de la carga de diferentes tipos de parámetros. Cuando hay más de un método de carga aplicable, el compilador también verifica la compatibilidad de la expresión de Lambda y el tipo de destino correspondiente. En pocas palabras, se espera que el método de Invoke anterior regrese, pero solo un método de Invoke tiene un valor de retorno.
Las expresiones de Lambda se pueden convertir explícitamente en tipos de objetivos especificados, siempre que sean compatibles con los tipos correspondientes. Al observar el siguiente programa, implementé tres tipos de invocación, y todos lo convirtieron en un tipo de clase.
Clase pública FirstWithLambdaExpressions {public static void main (string [] args) {listl = arrayList (callial)-> "llamado 1", (callar) ()-> "callable 2", (callable) (callable))-> "Callable 3"); ) {e1.printstackTrace ();} e.shutdown ();} public void dumplist (list list) lanza InterUptException, ExecutionExcepti on {for (Future Future: List) {System.out.println (futuro.get ()); }}}Como discutimos anteriormente, las categorías anónimas no pueden acceder a las variables no finales en el entorno circundante. Pero no existe tal límite en la expresión de lambda.
En la actualidad, las interfaces funcionales definidas solo son aplicables a la interfaz. Traté de crear una expresión lambda de un solo método abstracto, pero se cometió un error de compilación. Según JSR -335, la versión futura de la expresión de Lambda puede admitir clases funcionales.
Cita de método
Métodos referenciados como un método de referencia sin llamarlo.
La expresión de Lambda nos permite definir un método anónimo y usarlo como una instancia de la interfaz funcional. Los métodos son muy similares a la expresión de Lambda.
System :: GetProperty "ABC" :: PlayString :: Specificsuper :: ToStringarArrayList :: Nuevo
La declaración anterior muestra la sintaxis general del método y la referencia al constructor. Aquí hemos visto un nuevo personaje operativo "::::: Double Colon). No sé el nombre exacto como este operador, pero JSR se refiere a él como separadores, y la página de Wikipedia se refiere a él como una operación de análisis de alcance . Como referencia, dentro del rango de este tutorial, simplemente lo usaremos como separatista.
La referencia objetivo o el receptor se coloca detrás del proveedor y los separadores. Esto forma una expresión que puede citar un método. En la declaración final, el nombre del método es "nuevo". Esta expresión cita el método de estructura de la clase ArrayList (la referencia al constructor en la siguiente sección)
Antes de entender esto, quiero dejarle ver la fuerza del método citado.
Import java.util.arrays; = {Nuevo empleado ("nick"), nuevo empleado ("robin"), nuevo empleado ("josh"), nuevo empleado ("andy"), nuevo empleado ("marca")}; ANTES Sort: "); DumpEmployee (empleados); arrays.sort (empleados, empleado :: myCompare); system.out.println (" After Sort: "); Mployees);] Empleados) {para (Empleado EMP: Matrays. aslist (empleados)) {System.out.print (emp.name+",");} System.out.println (); (Empleado EMP1, Empleado EMP2) {return emp1.name.compareto (emp2.name);}} La salida es:
ANTES DE ANTES: Nick, Robin, Josh, Andy, Mark, After Sort: Andy, Josh, Mark, Nick, Robin,
La salida no es especial. Método estático MyCompare recibe dos objetos de empleados y devuelve sus nombres para comparar.
En el método principal, creé una matriz diferente de un empleado y la pasé al método de matriz.
Espere un minuto, si miramos a Javadoc, encontrará que el segundo parámetro del método de clasificación es el tipo de corarator, pero pasamos la referencia del método estático del empleado. El problema importante está aquí.
Echemos un vistazo a por qué. El método de matriz.sort espera una instancia de un comparador, y este comparador es una interfaz funcional, lo que significa que solo tiene un método, es decir, comparar. Aquí también pasamos maliciosamente una expresión de lambda, que proporciona la implementación del método Compaee en esta expresión. Pero en nosotros, nuestra clase de empleados ya tiene un método de comparación. Es solo que sus nombres son diferentes.
Cuando hay múltiples métodos del mismo nombre, el compilador elegirá la mejor coincidencia de acuerdo con el tipo de destino. Para entender, mira un ejemplo:
Public static int myCompare (Employee Emp1, Employee Emp2) {return emp1.name.compareto (emp2.name);} // Otro método con el mismo nombre que el anterior. {{) {{) {{Return int1.compareto (int2);} Creé dos matrices diferentes para clasificar.
Empleado [] empleado = {nuevo empleado ("nick"), nuevo empleado ("robin"), nuevo empleado ("josh"), nuevo empleado ("andy"), nuevo empleado ("marca")}; ins = {1, 4, 8, 2, 3, 8, 6}; Ahora, ejecuto las siguientes dos líneas de código
Arrays.sort (empleado, empleado :: myCompare);
Aquí, el método de referencias en las dos líneas de código es el mismo (Empleado :: MyCompare).
No se deje engañar por el método estático, también podemos crear una referencia al método de ejemplo. Para los métodos estáticos, usamos nombres de clase :: Nombre del método para escribir la referencia del método.
El ejemplo anterior es bastante bueno, pero no tenemos que escribir un método para la comparación de Integer, porque Integer se ha implementado comparable y proporciona un método de implementación Comparación. Entonces solo usamos la siguiente línea directamente:
Arrays.sort (ints, entero :: comparación);
Al ver esto, ¿te sientes un poco confundido? ¿No? Entonces déjame confundirte aquí. Se hace referencia al método de miembro :: Debería ser un objeto antes, pero por qué la oración aquí es realmente legítima.
La respuesta es: este tipo de declaración permite usar en algunos tipos específicos. Integer es un tipo de datos, y para el tipo de datos, esta declaración está permitida.
Si convirtimos el método de empleado myCompare en no estática, y luego usamos: Empleado :: myCompare, habrá errores de compilación: no se encontrará ningún método adecuado.
Referencia de método constructivo
Las referencias del constructor se usan como una clase que hace referencia a un constructor sin institucionalización especificada.
La referencia del método constructivo es una nueva característica de Javase 8. Podemos construir una referencia a un método constructivo y pasarlo como un parámetro al tipo de destino.
Cuando lo usamos para hacer referencia, citamos un método existente para usarlos. Del mismo modo, cuando se utilizan la referencia del método constructivo, creamos una referencia a los métodos constructivos existentes.
En la sección anterior, hemos visto el nombre de la gramática referenciado por el constructor :: nuevo, que parece una referencia de método. La referencia a este método construido se puede asignar a instancias de las interfaces funcionales de destino. Puede haber múltiples constructores en una clase.
Para mí, es difícil escribir el primer método constructivo. Al final, pasé mucho tiempo trabajando duro, y finalmente "ah, encontré ...", mira el siguiente procedimiento.
Public Class ConstructorReferences {public static void main (string [] ar) {myInterface in = myclass :: new; } La salida es:
-> com.myclass@34e5307e
Esto se ve un poco increíble, ¿verdad?
Este ejemplo despertó otro problema en mi corazón: ¿cómo instanciar un método constructivo con un parámetro? Mire el procedimiento a continuación:
Public Class ConstructorReferences {public static void main (string [] ar) {emlpoyeproder proveedor = empleado :: new; ; edad) {this.name = name; La salida es:
-> Nombre del empleado: John-> Edad del empleado: 30
Antes de leer este artículo, echemos un vistazo a la característica más genial del método de defensa de Javase8
Métodos predeterminados
Javase8 introducirá un concepto llamado método predeterminado. La versión temprana de Java de la interfaz tiene una interfaz muy estricta. En la próxima versión de Java, se permite la implementación predeterminada del método en la interfaz. No hay muchas tonterías, mira lo siguiente:
Public class DefaultMethods {public static void main (string [] ar) {NormalInterface instance = new NormalInterfaceImpl (); System.out.println ("-> mydefaultMethod");}} class NormalInterfaceImpl implemelemerInterface {@Override public void myNormalMethod () {) System.out.println ("-> myNormalMethod");}} La salida es:
-> mydefaultMethod
La interfaz anterior declara dos métodos, pero la clase de implementación de esta interfaz solo se da cuenta de uno de ellos, porque MyDefaultMethod usa los modificadores predeterminados, y proporciona un bloque de método para la implementación predeterminada. Las reglas de carga pesada de GM todavía entran en vigencia aquí. Si la clase de implementación implementa el método en la interfaz, será el método en la clase de llamadas cuando llame, de lo contrario, se llamará a la implementación predeterminada.
La interfaz de la interfaz principal integrada puede aumentar, cambiar y eliminar la implementación predeterminada de la interfaz principal.
Interfaz parentinterface {void initialnomal (); -> inicialmente nomal ");} void inicialmente deFault (); // Ahora un método normal} En este ejemplo, ParentInterface define dos métodos, uno es normal y el otro se implementa de forma predeterminada.
Imagine que una clase heredó la Clase C, se dio cuenta de que la interfaz I y C tenían un método, y el método que proporcionó el método predeterminado en el que proporcionó el método predeterminado era compatible. En este caso, el método en C dará prioridad al método predeterminado en I, e incluso el método en C sigue siendo prioritario cuando el método es abstracto.
Public Class DefaultMethods {public static void main (string [] ar) {interfaxe impl = new NormalInterfaceImpl (); ;}} interfaz interfaxe {public void defaultMethod () predeterminado {System.out.println ("-> Interfaxe"); La salida es:
-> Clase parent
El segundo ejemplo es que mi clase ha implementado dos interfaces diferentes, pero ambas interfaces proporcionan la misma declaración con el mismo método de implementación predeterminado. En este caso, el compilador no podrá averiguar qué está sucediendo. Esto se puede hacer de las siguientes maneras.
Public class DefaultMethods {public static void main (string [] ar) {FirstInterface impl = new NormalInterfampl (); );}} Interface SecondInterface {public void DefaultMethod () default {System.out.println ("-> SecondInterface"); La salida es:
-> Segunda Interface
Ahora, hemos leído la introducción del cierre de Java. En este artículo, entramos en contacto con interfaces funcionales y el cierre de Java, que entendía la expresión lambda de Java, las referencias de métodos y las referencias de constructor. Y también escribimos un ejemplo de Hello World de expresión de Lambda.
Javase8 llegará pronto.