java - ¿Por qué la traducción lambda necesita la generación de un método estático?
jvm java-8 (3)
Además de las respuestas correctas que se proporcionan aquí (debido a que el esquema actual es más eficiente, reduciendo los costos de captura / vinculación para las lambdas y reduciendo la duplicación de código), existen otras razones por las que su idea simplemente no tiene sentido.
- ¿De dónde vendría el bytecode en primer lugar? La clase de proxy lambda se genera en tiempo de ejecución, no en tiempo de compilación. Si tuviéramos que rellenar el bytecode en la clase de proxy, tendría que venir de alguna parte. Eso significaría que tendríamos que ponerlo en el archivo de la clase de captura y luego copiarlo en la clase de proxy. Aquí, solo vive en la clase de captura y hemos terminado.
- Control de acceso. ¿Qué pasa si el cuerpo lambda llama un método privado? Al desugarse en la clase de captura, adquiere automáticamente el contexto de control de acceso de la clase de captura (de lo que, lógicamente, forma parte de). Si colocamos el código de bytes en la clase de proxy, tendríamos que hacer magia adicional para darle El contexto de control de acceso correcto.
La traducción Lambda es un proceso de dos pasos, uno : desugaring la lambda en un método estático en la misma clase
public class Main {
public static void main(String[] args) {
Runnable r = () -> System.out.println("Hello");
System.out.println(Arrays.asList(Main.class.getDeclaredMethods()));
}
}
[ Private static void Main.lambda $ main $ 0 () , public static void Main.main (java.lang.String [])]
Dos : generación de una clase que implementa la interfaz funcional.
System.out.println("A class has been generated: " + r.getClass());
System.out.println("That implements a Functional Interface: " + Arrays.asList(r.getClass().getInterfaces()));
Se ha generado una clase: clase Main $$ Lambda $ 1/149928006
Eso implementa una interfaz funcional: [interface java.lang.Runnable]
Pregunta : ¿Cuál es la necesidad de este método estático? ¿Por qué no se puede poner el cuerpo lambda directamente en el método de interfaz? Algo como:
class Main$$Lambda$1 {
public void run() {
/* Lambda body here */
}
}
Porque así es más barato. Generar un lambda desde el método sobre la marcha durante la primera invocación es mejor que cargar una clase separada a través del cargador de clases. Internamente usa UNSAFE.defineAnonymousClass
que es más liviana de lo normal. Dicha "clase lambda" no está vinculada a ningún cargador de clases, por lo que puede recolectarse fácilmente cuando ya no es necesario. También creo que hay planes para hacer este mecanismo aún más ligero y más rápido. Para una clase anónima normal, esto no sería posible ya que, desde el punto de vista de la JVM, tales clases no difieren de las clases habituales y son mucho más pesadas.
Su prueba está incompleta.
public class Lambda {
private String hello = "Hello from instance";
public static void main(String[] args) {
Runnable r = () -> System.out.println("Hello");
for (Method m: Lambda.class.getDeclaredMethods()) {
System.out.println(m);
}
}
public void instanceMethodWithAccessToInstanceVariables(){
Runnable r = () -> System.out.println(hello);
}
public void instanceMethodWithoutAccessToInstanceVariables(){
Runnable r = () -> System.out.println("Hello from instance");
}
}
Esto resulta en lo siguiente:
public void Lambda.instanceMethodWithAccessToInstanceVariables()
public void Lambda.instanceMethodWithoutAccessToInstanceVariables()
private static void Lambda.lambda$instanceMethodWithoutAccessToInstanceVariables$2()
private void Lambda.lambda$instanceMethodWithAccessToInstanceVariables$1()
private static void Lambda.lambda$main$0()
public static void Lambda.main(java.lang.String[])
Esto muestra claramente varios casos:
- Las lambdas en método estático declaran método estático.
- lambdas en los métodos de instancia que utilizan variables de instancia declaran métodos de instancia
- los lambdas en los métodos de instancia que no utilizan variables de instancia declaran método estático
Los dos primeros son bastante lógicos. ¿Por qué esperarías que un miembro estático acceda a un miembro de instancia? Lo mismo para el método de instancia.
La pregunta real es ¿por qué un método de instancia que no usa variables de instancia declara un método estático?
Bueno, esto también es por razones de rendimiento y seguridad mencionadas por Tagir.