¿Las referencias a métodos omiten la sobrecarga del contenedor lambda? ¿Podrían ellos en el futuro?
De acuerdo con el Tutorial de Java sobre referencias de métodos :
A veces ... una expresión lambda no hace más que llamar a un método existente. En esos casos, a menudo es más claro referirse al método existente por su nombre. Las referencias de métodos le permiten hacer esto; son expresiones lambda compactas y fáciles de leer para métodos que ya tienen un nombre.
Prefiero la sintaxis lambda a la sintaxis de referencia del método por varias razones:
Las lambdas son más claras
A pesar de las afirmaciones de Oracle, encuentro que la sintaxis lambda es más fácil de leer que la referencia breve al método de objeto porque la sintaxis de referencia del método es ambigua:
Bar::foo
¿Está llamando a un método estático de un argumento en la clase de x y pasándole x?
x -> Bar.foo(x)
¿O estás llamando a un método de instancia de argumento cero en x?
x -> x.foo()
La sintaxis de referencia del método podría sustituir a cualquiera de los dos. Oculta lo que su código está haciendo realmente.
Las lambdas son más seguras
Si hace referencia a Bar :: foo como un método de clase y luego Bar agrega un método de instancia con el mismo nombre (o viceversa), su código ya no se compilará.
Puedes usar lambdas constantemente
Puede ajustar cualquier función en un lambda, por lo que puede usar la misma sintaxis de manera consistente en todas partes. La sintaxis de referencia del método no funcionará en los métodos que toman o devuelven matrices primitivas, lanzan excepciones verificadas o tienen el mismo nombre de método utilizado como una instancia y un método estático (porque la sintaxis de referencia del método es ambigua sobre qué método se llamaría) . No funcionan cuando ha sobrecargado los métodos con el mismo número de argumentos, pero no debe hacerlo de todos modos (consulte el artículo 41 de Josh Bloch), por lo que no podemos compararlo con las referencias de métodos.
Conclusión
Si no hay una penalización de rendimiento por hacerlo, estoy tentado a desactivar la advertencia en mi IDE y usar la sintaxis lambda de manera consistente sin rociar la referencia de método ocasional en mi código.
PD
Ni aquí ni allá, pero en mis sueños, las referencias a métodos de objetos se parecen más a esto y aplican invoke-dynamic contra el método directamente en el objeto sin un contenedor lambda:
_.foo()
.map(_.acceptValue())
: Si no me equivoco, esto se parece mucho a la sintaxis de Scala. Tal vez solo estás tratando de usar el lenguaje incorrecto.System.out::println
aforEach()
todo el tiempo ...?Respuestas:
En muchos escenarios, creo que lambda y referencia de método son equivalentes. Pero la lambda ajustará el objetivo de invocación por el tipo de interfaz declarante.
Por ejemplo
Verá la salida de la consola del stacktrace.
En
lambda()
, el método que llamatarget()
eslambda$lambda$0(InvokeTest.java:20)
, que tiene información de línea rastreable. Obviamente, esa es la lambda que escribes, el compilador genera un método anónimo para ti. Y luego, la persona que llama del método lambda es algo así comoInvokeTest$$Lambda$2/1617791695.run(Unknown Source)
, es lainvokedynamic
llamada en JVM, significa que la llamada está vinculada al método generado .En
methodReference()
, la llamada al métodotarget()
es directamenteInvokeTest$$Lambda$1/758529971.run(Unknown Source)
, significa que la llamada está directamente vinculada alInvokeTest::target
método .Conclusión
Sobre todo, compárelo con referencia de método, el uso de la expresión lambda solo causará una llamada más al método de generación desde lambda.
fuente
Se trata de la metafactory
Primero, la mayoría de las referencias de métodos no necesitan ser desvanecidas por la metafábrica lambda, simplemente se usan como método de referencia. En la sección "Azúcar corporal Lambda" del artículo de la Traducción de las expresiones Lambda ("TLE"):
Esto se resalta aún más abajo en "The Lambda Metafactory" de TLE:
Las referencias de un
Integer::sum
método de instancia estático ( ) o sin límite (Integer::intValue
) son las 'más simples' o las más 'convenientes', en el sentido de que pueden ser manejadas de manera óptima por una variante metafábrica de 'vía rápida' sin el desvanecimiento . Esta ventaja se señala de manera útil en las "variantes metafactorias" de TLE:Naturalmente, una referencia de método de captura de instancias (
obj::myMethod
) necesita proporcionar la instancia acotada como un argumento para el identificador del método para invocación, lo que puede significar la necesidad de desugar el uso de métodos 'puente'.Conclusión
No estoy exactamente seguro de cuál es el 'envoltorio' lambda al que estás insinuando, pero a pesar de que el resultado final de usar tus lambdas o referencias de método definidas por el usuario es el mismo, la forma en que se alcanza parece ser bastante diferente, y puede ser diferente en el futuro si ese no es el caso ahora. Por lo tanto, supongo que es más probable que las referencias a métodos puedan ser manejadas de manera más óptima por la metafábrica.
fuente
Hay una consecuencia bastante grave cuando se usan expresiones lambda que pueden afectar el rendimiento.
Cuando declara una expresión lambda, está creando un cierre sobre el ámbito local.
¿Qué significa esto y cómo afecta el rendimiento?
Bueno, me alegra que lo hayas preguntado. Significa que cada una de esas pequeñas expresiones lambda es una pequeña clase interna anónima y eso significa que lleva consigo una referencia a todas las variables que están en el mismo alcance de la expresión lambda.
Esto significa también
this
referencia de la instancia del objeto y todos sus campos. Dependiendo del momento de la invocación real de la lambda, esto puede equivaler a una pérdida de recursos bastante significativa ya que el recolector de basura no puede soltar estas referencias mientras el objeto que se mantiene en una lambda todavía esté vivo ...fuente
this
objeto al que se podría hacer referencia. Una lambda, por lo tanto, no pierde recursos simplemente por tener un alcance para ellos; solo se aferra a los objetos que necesita. Por otro lado, una clase interna anónima puede perder recursos, pero no siempre lo hace. Consulte este código para ver un ejemplo: a.blmq.us/2mmrL6v