Les références aux méthodes ignorent-elles les frais généraux du wrapper lambda? Pourraient-ils dans le futur?
Selon le tutoriel Java sur les références de méthode :
Parfois ... une expression lambda ne fait qu'appeler une méthode existante. Dans ces cas, il est souvent plus clair de faire référence à la méthode existante par son nom. Les références de méthode vous permettent de le faire; Ce sont des expressions lambda compactes et faciles à lire pour les méthodes qui ont déjà un nom.
Je préfère la syntaxe lambda à la syntaxe de référence de la méthode pour plusieurs raisons:
Lambdas sont plus claires
Malgré les affirmations d'Oracle, je trouve la syntaxe lambda plus facile à lire que la référence à la méthode de référence de l'objet car la syntaxe de référence de la méthode est ambiguë:
Bar::foo
Appelez-vous une méthode statique à un argument sur la classe de x et transmettez-vous x?
x -> Bar.foo(x)
Ou appelez-vous une méthode d'instance à zéro argument sur x?
x -> x.foo()
La syntaxe de référence de la méthode peut remplacer l'un ou l'autre. Il cache ce que votre code est en train de faire.
Lambdas sont plus sûrs
Si vous faites référence à Bar :: foo en tant que méthode de classe et que Bar ajoute ultérieurement une méthode d'instance du même nom (ou inversement), votre code ne sera plus compilé.
Vous pouvez utiliser des lambdas de manière constante
Vous pouvez envelopper n'importe quelle fonction dans un lambda - afin que vous puissiez utiliser la même syntaxe partout. La syntaxe de référence de méthode ne fonctionne pas avec les méthodes qui prennent ou renvoient des tableaux primitifs, lèvent des exceptions vérifiées ou ont le même nom de méthode utilisé comme instance et une méthode statique (car la syntaxe de référence de méthode est ambiguë quant à la méthode à appeler) . Ils ne fonctionnent pas lorsque vous avez surchargé des méthodes avec le même nombre d'arguments, mais vous ne devriez pas le faire de toute façon (voir le point 41 de Josh Bloch), nous ne pouvons donc pas en tenir autant aux références à des méthodes.
Conclusion
Si cela ne nuit pas aux performances, je suis tenté de désactiver l'avertissement dans mon IDE et d'utiliser la syntaxe lambda de manière cohérente sans saupoudrer la référence de méthode occasionnelle dans mon code.
PS
Ni ici ni là-bas, mais dans mes rêves, les références de méthode d'objet ressemblent davantage à ceci et appliquent invoke-dynamic à la méthode directement sur l'objet sans wrapper lambda:
_.foo()
.map(_.acceptValue())
: Si je ne me trompe pas, cela ressemble beaucoup à la syntaxe Scala. Peut-être que vous essayez simplement d'utiliser le mauvais langage.System.out::println
àforEach()
tout le temps ...?Réponses:
Dans de nombreux scénarios, je pense que lambda et method-reference sont équivalents. Mais le lambda encapsulera la cible d'invocation par le type d'interface déclarant.
Par exemple
Vous verrez la console sortir le stacktrace.
Dans
lambda()
, l'appel de méthodetarget()
estlambda$lambda$0(InvokeTest.java:20)
, qui a des informations de ligne traçables. Évidemment, c’est le lambda que vous écrivez, le compilateur génère une méthode anonyme pour vous. Et puis, l'appelant de la méthode lambda ressemble à quelque chose commeInvokeTest$$Lambda$2/1617791695.run(Unknown Source)
: c'est l'invokedynamic
appel de la machine virtuelle Java, cela signifie que l'appel est lié à la méthode générée .En
methodReference()
appelanttarget()
directement la méthodeInvokeTest$$Lambda$1/758529971.run(Unknown Source)
, cela signifie que l'appel est directement lié à laInvokeTest::target
méthode .Conclusion
Surtout, comparez-vous à la méthode-référence, utiliser l'expression lambda ne provoquera qu'un seul appel supplémentaire à la méthode génératrice à partir de lambda.
la source
Tout tourne autour de la méta - usine
Premièrement, la plupart des références de méthode n’ont pas besoin d’être étudiées par la métafactory lambda, elles sont simplement utilisées comme méthode de référence. Sous la section "Sucre du corps Lambda" de l'article sur la traduction des expressions lambda ("TLE"):
Ceci est davantage souligné plus bas dans "The Lambda Metafactory" de TLE:
Les références à une
Integer::sum
méthode statique ( ) ou à une instance non liée (Integer::intValue
) sont les plus simples ou les plus pratiques, dans la mesure où elles peuvent être gérées de manière optimale par une variante métafabrique à «chemin rapide» sans désugaration . Cet avantage est utilement souligné dans les "variantes Metafactory" de TLE:Naturellement, une méthode de capture d'instance reference (
obj::myMethod
) doit fournir l'instance liée en tant qu'argument au descripteur de méthode pour l'invocation, ce qui peut signifier la nécessité de désinsectiser à l'aide de méthodes 'bridge'.Conclusion
Je ne suis pas tout à fait sûr de la "enveloppe" lambda à laquelle vous faites allusion, mais même si le résultat final de l'utilisation de vos lambdas définis par l'utilisateur ou de vos références de méthode est le même, la méthode atteinte semble être très différente, et peut être différent à l'avenir si ce n'est pas le cas maintenant. Par conséquent, je suppose qu'il est plus probable qu'improbable que la méta-usine puisse traiter les références de méthode de manière plus optimale.
la source
Il y a une conséquence assez grave lorsqu’on utilise des expressions lambda qui peut affecter les performances.
Lorsque vous déclarez une expression lambda, vous créez une fermeture sur la portée locale.
Qu'est-ce que cela signifie et en quoi cela affecte-t-il les performances?
Eh bien, je suis content que vous avez demandé. Cela signifie que chacune de ces petites expressions lambda est une petite classe interne anonyme, ce qui signifie qu'elle comporte une référence à toutes les variables qui appartiennent au même champ d'application de l'expression lambda.
Cela signifie également la
this
référence de l'instance d'objet et de tous ses champs. En fonction du moment de l'invocation réelle du lambda, cela peut constituer une fuite de ressource assez importante car le garbage collector est incapable de lâcher ces références tant que l'objet contenant un lambda est toujours en vie ...la source
this
objet qui pourrait être référencé. Un lambda ne perd donc pas de ressources simplement en ayant la possibilité de les utiliser; il ne conserve que les objets dont il a besoin. D'autre part, une classe interne anonyme peut perdre des ressources, mais ne le fait pas toujours. Voir ce code pour un exemple: a.blmq.us/2mmrL6v