¿Es una expresión lambda algo más que una clase interna anónima con un solo método?

40

Hay un nuevo bombo con las expresiones lambda tan esperadas en Java 8; cada 3 días aparece otro artículo con ellos sobre lo geniales que son.

Por lo que he entendido, una expresión lambda no es más que una clase interna anónima con un único método (al menos en el nivel de código de bytes). Además de esto, viene con otra característica interesante: la inferencia de tipos, pero creo que el equivalente de esto se puede lograr con genéricos en algún nivel (por supuesto, no de una manera tan clara como con las expresiones lambda).

Sabiendo esto, ¿las expresiones lambda traerán algo más que un simple azúcar sintáctico en Java? ¿Puedo crear clases más potentes y flexibles u otras construcciones orientadas a objetos con expresiones lambda que no se puedan construir con las características del lenguaje actual?

m3th0dman
fuente
30
Cuando un lenguaje está completo, cada característica adicional podría describirse teóricamente como "azúcar sintáctico".
Philipp
34
@Philipp: Eso está mal. La característica definitoria del azúcar sintáctico es que el desagüe es puramente local y no cambia la estructura global del programa. Hay muchas características que no se pueden implementar solo con transformaciones locales. Por ejemplo, si toma un lenguaje hipotético que es idéntico a Java pero con llamadas de cola adecuadas, no sería posible desugar las llamadas de cola a Java "puro" sin transformar globalmente todo el programa.
Jörg W Mittag
2
@Philipp: Lamentablemente, solo hay un voto a favor para los comentarios, de lo contrario, votaría el comentario de Joerg 1000 veces. No, está mal que cualquier característica pueda describirse como azúcar sintáctica. El azúcar sintáctico no agrega nueva semántica. De hecho, AFAIK las lambdas de Java propuestas NO son azúcar sintáctica para clases internas anónimas especiales porque tienen una semántica diferente.
Giorgio
1
@Giorgio: ¿y qué semántica diferente tienen?
user102008
2
@Giorgio: Sí, pero la conversión de thisinto OuterClass.thises parte del proceso de quitar el azúcar de las expresiones lambda a una clase anónima.
user102008

Respuestas:

47

tl; dr: si bien es principalmente azúcar sintáctico, esa sintaxis más agradable hace que muchas cosas sean prácticas y que solían terminar en interminables e ilegibles líneas de llaves y paréntesis.

Bueno, en realidad es al revés, ya que las lambdas son mucho más antiguas que Java. Las clases internas anónimas con un único método son (fueron) las más cercanas a Java que llegaron a lambdas. Es una aproximación que fue "suficientemente buena" durante algún tiempo, pero tiene una sintaxis muy desagradable.

En la superficie, las lambdas de Java 8 no parecen ser mucho más que azúcar sintáctica, pero cuando miras debajo de la superficie, ves toneladas de abstracciones interesantes. Por ejemplo, la especificación JVM trata una lambda de manera muy diferente a un objeto "verdadero", y aunque puede manejarlos como si fueran objetos, no es necesario que la JVM los implemente como tal.

Pero aunque todo ese truco técnico es interesante y relevante (¡ya que permite futuras optimizaciones en la JVM!), El beneficio real es "solo" la parte sintáctica del azúcar.

Lo que es más fácil de leer:

myCollection.map(new Mapper<String,String>() {
  public String map(String input) {
    return new StringBuilder(input).reverse().toString();
  }
});

o:

myCollection.map(element -> new StringBuilder(element).reverse().toString());

o (usando un identificador de método en lugar de una lambda):

myCollection.map(String::toUpperCase);

El hecho de que finalmente pueda expresar de manera concisa lo que anteriormente serían 5 líneas de código (de las cuales 3 son completamente aburridas) trae un cambio real de lo que es práctico (pero no de lo que es posible, concedido).

Joachim Sauer
fuente
1
Estoy totalmente de acuerdo con la parte de sintaxis de azúcar; mi pregunta era sobre nuevas construcciones orientadas a objetos solo posibles con expresiones lambda; después de todo, Java es un lenguaje orientado a objetos. Piense en comparación con los genéricos, y cómo compraron mucha flexibilidad en Java, flexibilidad inalcanzable sin ellos.
m3th0dman
77
@ m3th0dman: en realidad, los genéricos no agregaron nuevas habilidades. A Listpodría contener cualquier objeto, un List<String>"solo" puede contener cadenas. Los genéricos agregaron una restricción (¡y la opción de formalizar restricciones!), No una adición en el poder. Casi todo desde Java 1.1 ha sido el azúcar sintáctico. Y eso no es necesariamente algo malo.
Joachim Sauer
3
@ m3th0dman: En realidad, los genéricos no agregaron nuevas habilidades, porque en Java son solo un azúcar sintáctico. A List<String>es solo un Listreparto para Stringagregar alrededor de algunos valores de retorno. Sin embargo, son extremadamente útiles y hacen que el lenguaje sea mucho más conveniente para escribir. Las lambdas son un caso similar.
Jan Hudec
3
En realidad, es mucho más que azúcar sintáctica. Gran parte de la funcionalidad se implementa como un ciudadano de primera clase en la JVM mediante el uso del bytecode invocado dinámico + algunos avances bastante importantes para el sistema de inferencia de tipos. No se implementa como clases internas anónimas.
Martijn Verburg
1
@ JanHudec: bueno, son un poco más que azúcar sintáctica, porque el compilador realmente los respeta. El tiempo de ejecución no se preocupa por ellos, eso es cierto.
Joachim Sauer
14

Para Java, sí, no es más que una mejor manera de crear una clase interna anónima. Esto se debe a la decisión fundamental en Java de que cada bit de código de bytes tiene que vivir dentro de una clase específica, que no se puede cambiar ahora después de décadas de código heredado a tener en cuenta.

Sin embargo, de eso no se tratan realmente las expresiones lambda. En los formalismos donde son conceptos nativos en lugar de una contorsión de subirse al carro, las lambdas son bloques de construcción fundamentales; Tanto su sintaxis como la actitud de las personas hacia ellos son muy diferentes. El ejemplo de una función recursiva anónima creada exclusivamente a partir de lambdas en Estructura e interpretación de programas de computadora es capaz de cambiar toda su concepción de la computación. (Sin embargo, estoy bastante seguro de que la forma de aprender a programar es simplemente esotérica para convertirse en una historia de éxito convencional).

Kilian Foth
fuente
1
Aprender que todo se puede implementar en términos de lambdas es muy instructivo y no "esotérico" en mi opinión. (Sin embargo, supongo que a la mayoría de los "codificadores" no les importa la teoría CS interesante). Eso no significa que lambdas sea especial; solo significa que las lambdas son un tipo de construcción universal. Hay otros, como los combinadores de SKI. Las lambdas son bloques de construcción fundamentales en paradigmas funcionales, pero tal vez algo más puede ser fundamental en otro paradigma.
user102008
+1 para "contorsión de subirse al carro": a menudo me pregunto por qué algunos idiomas siguen cambiando para imitar a otros lenguajes populares y por qué los programadores lo soportan. Me gustan las lambdas y las uso mucho en Scala, Lisp, Haskell, pero en Java o C ++ se sienten como una idea tardía en el idioma solo porque se han vuelto populares en otros idiomas.
Giorgio
2

Sí, es solo un azúcar sintáctico, en el sentido de que en cualquier lugar donde escriba un lambda, puede volver a escribir esa expresión como una expresión de clase interna anónima con un método, donde la clase implementa la interfaz funcional inferida para el contexto del lambda, y sería exactamente equivalente semánticamente. Y puede hacer esto simplemente reemplazando la expresión lambda con la expresión de clase anónima, sin cambiar ninguna otra expresión o línea de código.

usuario102008
fuente
1
¿Por qué rechazar? lo que dije es completamente correcto
user102008
Lo que escribe parece una propuesta razonable para las lambdas de Java, pero esta propuesta se descartó en favor de otra diferente ( doanduyhai.wordpress.com/2012/07/12/… ).
Giorgio
1
@Giorgio: No estoy "proponiendo" nada. ¿Qué, exactamente, no estás de acuerdo con lo que dije? Sí, el interior de la expresión se verá diferente, por ejemplo, agregaremos un método y sí, thisserá necesario convertirlo OuterClass.this. Eso no contradice lo que dije: cada expresión lambda se puede convertir en una expresión de clase anónima semánticamente equivalente sin alterar nada fuera de esa expresión . No dije que el interior no cambiaría.
user102008
3
Lo que dijiste es incorrecto. La semántica de las clases internas lambda y anon no es exactamente la misma (aunque son similares). Fuera de mi cabeza, el significado del significado de esto cambia; y anon clases internas siempre dan como resultado una nueva instancia de un objeto, mientras que un lambda podría o no.
Stuart Marks
1
@StuartMarks: No, no leíste mi respuesta. No dije que cada uno de ellos puede hacer todo lo que el otro hace. Dije que una lambda tiene una clase anónima equivalente que es semánticamente igual. Como ya dije en los comentarios, thisen lambda es parte del azúcar sintáctico, y no es una diferencia en la semántica. Y sobre las diferencias en la implementación, implementación! = Semántica. Sobre las diferencias en la identidad del objeto; la identidad del objeto es extremadamente tangencial a la funcionalidad de lambdas; nadie comprueba la identidad del objeto de las clases lambdas / anon de todos modos porque no es útil.
user102008
1

Joachim Sauer ya hizo un buen trabajo respondiendo su pregunta, pero solo insinuó algo que considero importante. Como las Lambdas no son clases, tampoco se compilan como tales. Todas las clases internas anónimas dan como resultado la creación de un archivo .class que a su vez debe cargarlo el ClassLoader. Por lo tanto, usar Lambdas en su lugar no solo hace que su código sea más hermoso, sino que también reduce el tamaño de su código compilado, la huella de memoria de su ClassLoader y el tiempo que toma transferir los bits de su disco duro.

Christoph Grimmer-Dietrich
fuente
-1

No, ellos no son.

Otra forma de decirlo es que las lambdas son una forma no orientada a objetos, aunque concisa, de lograr lo mismo que una clase anónima o, mi preferencia, la clase interna lograría de una manera orientada a objetos.

Guido Anselmi
fuente
1
La programación funcional puede ser totalmente ortogonal a la programación orientada a objetos (ver: Scala y F #). Esto se lee como la opinión de alguien a quien simplemente no le gusta la programación funcional por principio.
KChaloux
1
@KChaloux: no es un enemigo de la programación funcional. La respuesta está escrita por alguien que prefiere tener un martillo y un destornillador en lugar de un martillo. La programación OO es un buen paradigma, como lo es la programación funcional. Mi preferencia en un idioma es que sea claro sobre sus intenciones. Lambdas Me siento embarrado por la naturaleza OO de Java. Java no fue diseñado para ser un lenguaje funcional. Los seres humanos necesitan estructura para hacer su mejor trabajo.
Guido Anselmi