java - programacion - lambda ejemplo
Java8 Lambdas vs clases anónimas (5)
Dado que Java8 se lanzó recientemente y sus nuevas expresiones lambda parecen ser geniales, me preguntaba si esto significaría la desaparición de las clases anónimas a las que estábamos acostumbrados.
He estado investigando un poco acerca de esto y he encontrado algunos buenos ejemplos sobre cómo las expresiones de Lambda reemplazarán sistemáticamente esas clases, como el método de clasificación de Collection, que solía obtener una instancia anónima de Comparator para realizar el tipo:
Collections.sort(personList, new Comparator<Person>(){
public int compare(Person p1, Person p2){
return p1.firstName.compareTo(p2.firstName);
}
});
Ahora se puede hacer usando Lambdas:
Collections.sort(personList, (Person p1, Person p2) -> p1.firstName.compareTo(p2.firstName));
Y parece sorprendentemente conciso. Entonces mi pregunta es, ¿hay alguna razón para seguir usando esas clases en Java8 en lugar de Lambdas?
EDITAR
La misma pregunta, pero en la dirección opuesta, ¿cuáles son los beneficios de usar Lambdas en lugar de clases anónimas, ya que Lambdas solo se puede usar con interfaces de método único, esta nueva función es solo un atajo que solo se usa en algunos casos o es realmente útil?
Aunque Lambdas es una gran característica, solo funcionará con los tipos de SAM. Es decir, interactúa con un solo método abstracto. Fallará tan pronto como su interfaz contenga más de 1 método abstracto. Ahí es donde las clases anónimas serán útiles.
Entonces, no, no podemos simplemente ignorar las clases anónimas. Y solo para su información, su método de sort()
puede simplificarse más, omitiendo la declaración de tipo para p1
y p2
:
Collections.sort(personList, (p1, p2) -> p1.firstName.compareTo(p2.firstName));
También puede usar la referencia de método aquí. O agregas un método compareByFirstName()
en la clase Person
y usas:
Collections.sort(personList, Person::compareByFirstName);
o bien, agregue un getter para firstName
, obtenga directamente el Comparator
del método Comparator.comparing()
:
Collections.sort(personList, Comparator.comparing(Person::getFirstName));
Hay las siguientes diferencias:
1) Sintaxis
Las expresiones Lambda se ven bien en comparación con la clase interna anónima (AIC)
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("in run");
}
};
Thread t = new Thread(r);
t.start();
}
//syntax of lambda expression
public static void main(String[] args) {
Runnable r = ()->{System.out.println("in run");};
Thread t = new Thread(r);
t.start();
}
2. Alcance
Una clase interna anónima es una clase, lo que significa que tiene alcance para variable definida dentro de la clase interna.
Mientras que, la expresión lambda no es un ámbito propio, sino que es parte del alcance adjunto.
Se aplica una regla similar para super y esta palabra clave cuando se usa dentro de clase interna anónima y expresión lambda. En el caso de una clase interna anónima, esta palabra clave se refiere al alcance local y la palabra clave super hace referencia a la superclase de la clase anónima. Mientras que en el caso de la expresión lambda, esta palabra clave se refiere al objeto del tipo adjunto y super hará referencia a la superclase de la clase adjunta.
//AIC
public static void main(String[] args) {
final int cnt = 0;
Runnable r = new Runnable() {
@Override
public void run() {
int cnt = 5;
System.out.println("in run" + cnt);
}
};
Thread t = new Thread(r);
t.start();
}
//Lambda
public static void main(String[] args) {
final int cnt = 0;
Runnable r = ()->{
int cnt = 5; //compilation error
System.out.println("in run"+cnt);};
Thread t = new Thread(r);
t.start();
}
3) Rendimiento
En el tiempo de ejecución, las clases internas anónimas requieren carga de clases, asignación de memoria e inicialización de objetos e invocación de un método no estático mientras que la expresión lambda es pura actividad de tiempo de compilación y no incurre en costos adicionales durante el tiempo de ejecución. Por lo tanto, el rendimiento de la expresión lambda es mejor que el de las clases internas anónimas **.
** Me doy cuenta de que este punto no es del todo cierto. Por favor refiérase a la siguiente pregunta para más detalles. ¿Desempeño Lambda vs clase interna anónima: reducir la carga en el ClassLoader?
Lambda''s in java 8 fue presentado para la programación funcional. Donde puedes evitar el código repetitivo. Encontré este interesante artículo sobre lambda''s.
http://radar.oreilly.com/2014/04/whats-new-in-java-8-lambdas.html
Es aconsejable usar funciones lambda para lógicas simples. Si la implementación de una lógica compleja usando lambdas será una sobrecarga en la depuración del código en caso de problema.
Una clase interna anónima (AIC) puede usarse para crear una subclase de una clase abstracta o una clase concreta. Un AIC también puede proporcionar una implementación concreta de una interfaz, incluida la adición de estado (campos). Se puede hacer referencia a una instancia de un AIC mediante el uso de this
en sus cuerpos de método, por lo que se pueden solicitar otros métodos, su estado puede mutarse en el tiempo, etc. Ninguno de estos se aplica a lambdas.
Supongo que la mayoría de los usos de los AIC debían proporcionar implementaciones sin estado de funciones únicas y, por lo tanto, pueden reemplazarse con expresiones lambda, pero existen otros usos de los AIC para los que no se pueden usar lambdas. Los AIC llegaron para quedarse.
ACTUALIZAR
Otra diferencia entre los AIC y las expresiones lambda es que los AIC introducen un nuevo alcance. Es decir, los nombres se resuelven desde las superclases e interfaces de AIC y pueden sombrear los nombres que se producen en el entorno lexcial envolvente. Para lambdas, todos los nombres se resuelven léxicamente.
Rendimiento de Lambda con clases anónimas
Cuando se inicia la aplicación, cada archivo de clase debe cargarse y verificarse.
El compilador procesa las clases anónimas como un nuevo subtipo para la clase o interfaz determinada, por lo que se generará un nuevo archivo de clase para cada una.
Las lambdas son diferentes en la generación de bytecode, son más eficientes, se utilizan instrucciones dinámicas invocadas que vienen con JDK7.
Para Lambdas, esta instrucción se usa para retrasar la traducción de la expresión lambda en bytecode hasta el tiempo de ejecución. (instrucción será invocada por primera vez solamente)
Como resultado, la expresión Lambda se convertirá en un método estático (creado en tiempo de ejecución). (Hay una pequeña diferencia con los stateles y los statefull, se resuelven mediante argumentos de métodos generados)