¿Se compilan las lambdas de Java 8 como clases internas, métodos u otra cosa?
java-8 (4)
La expresión en sí, suponiendo que pase una
expresión lambda real
y no una referencia de método, se compila como un método sintético separado.
Además de cualquier argumento formal a la interfaz funcional esperada (por ejemplo, una sola
String
en el caso de
Consumer<String>
), incluirá argumentos para cualquier valor capturado.
En la ubicación del código donde aparece una expresión lambda o referencia de método, se emite una instrucción
invokedynamic
.
La primera vez que se golpea esta instrucción, se realiza una llamada en un método de arranque en
LambdaMetafactory
.
Este método de arranque arreglará una implementación real de la interfaz funcional de destino que delega al método de destino, y esto es lo que se devuelve.
El método de destino es el método sintético que representa el cuerpo lambda o el método nombrado que se proporcionó utilizando el operador
::
.
Mientras
se
crea una clase que implementa la interfaz funcional, el proceso se difiere;
No sucede en tiempo de compilación.
Finalmente, el tiempo de ejecución
invokedynamic
sitio
invokedynamic
con el resultado de bootstrap
1
, que es efectivamente una llamada de constructor al delegado generado con cualquier valor capturado pasado, incluido (posiblemente) un objetivo de invocación
2
.
Esto alivia el impacto en el rendimiento al eliminar el proceso de arranque para las llamadas posteriores.
1 Ver java.lang.invoke al final del capítulo "sincronización del enlace" , cortesía de @Holger.
2
En el caso de una lambda que no captura, la instrucción
invokedynamic
generalmente se resolverá en una instancia de delegado compartida que se puede reutilizar durante las llamadas posteriores, aunque este es un detalle de implementación.
Hoy he leído este artículo sobre lambdas:
http://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood
El artículo sugiere que las lambdas no se implementan como clases internas anon (debido al rendimiento). Da un ejemplo de que una expresión lambda puede compilarse como un método de clase (estático).
He probado un fragmento muy simple:
private void run() {
System.out.println(this);
giveHello(System.out::println);
}
private void giveHello(Consumer<String> consumer) {
System.out.println(consumer);
consumer.accept("hello");
}
y la salida es:
sample.Main@14ae5a5
sample.Main$$Lambda$1/168423058@4a574795
hello
Entonces no es la misma instancia. Tampoco es una instancia central de "Fábrica Lambda".
¿Cómo se implementan las lambdas entonces?
Las Lambda en Java 8 se denominan interfaces funcionales, es decir, interfaces anónimas con un método
default
.
Me hice la misma pregunta y encontré este video , una charla de Brian Goetz. Es una introducción muy útil a cómo se implementan las lambdas en Java.
Editar (resumen): lo vi hace un tiempo, por lo que esto podría no ser completamente correcto. Cuando se compilan los archivos, el compilador deja una descripción de lo que debe hacer el lambda. El JRE, luego cuando ejecuta el código, decidirá cómo implementar el lambda. Hay varias opciones, en línea, referencia de método, clase anónima. Luego utilizará la asignación dinámica para asignar la implementación.
No estoy seguro, pero creo que solo se está compilando en la clase interna Anónimo.
Consumer<T>
es una interfaz, por lo que diría que su ejemplo es casi igual a
giveHello(new Consumer<String>() {
@Override
public void accept(String t) {
System.out.println(t);
}
});
EDITAR
Después de algunas investigaciones, la respuesta anterior no es completa y válida. Las expresiones lambda pueden traducirse a una clase interna anónima, pero no tienen que hacerlo (y generalmente no lo hacen).