J'ai une question concernant l'utilisation de la Function.identity()
méthode.
Imaginez le code suivant:
Arrays.asList("a", "b", "c")
.stream()
.map(Function.identity()) // <- This,
.map(str -> str) // <- is the same as this.
.collect(Collectors.toMap(
Function.identity(), // <-- And this,
str -> str)); // <-- is the same as this.
Y a-t-il une raison pour laquelle vous devriez utiliser à la Function.identity()
place de str->str
(ou vice versa). Je pense que la deuxième option est plus lisible (une question de goût bien sûr). Mais, y a-t-il une «vraie» raison pour laquelle on devrait être préféré?
java
lambda
java-8
java-stream
Przemysław Głębocki
la source
la source
t -> t
simplement parce que c'est plus succinct.Réponses:
À partir de l'implémentation JRE actuelle,
Function.identity()
retournera toujours la même instance tandis que chaque occurrence deidentifier -> identifier
créera non seulement sa propre instance mais aura même une classe d'implémentation distincte. Pour plus de détails, voir ici .La raison en est que le compilateur génère une méthode synthétique contenant le corps trivial de cette expression lambda (dans le cas de
x->x
, équivalent àreturn identifier;
) et indique au runtime de créer une implémentation de l'interface fonctionnelle appelant cette méthode. Le runtime ne voit donc que différentes méthodes cibles et l'implémentation actuelle n'analyse pas les méthodes pour savoir si certaines méthodes sont équivalentes.Donc, utiliser
Function.identity()
au lieu dex -> x
pourrait économiser de la mémoire, mais cela ne devrait pas motiver votre décision si vous pensez vraiment que c'estx -> x
plus lisible queFunction.identity()
.Vous pouvez également considérer que lors de la compilation avec les informations de débogage activées, la méthode synthétique aura un attribut de débogage de ligne pointant vers la ou les lignes de code source contenant l'expression lambda, vous avez donc une chance de trouver la source d'une
Function
instance particulière lors du débogage . En revanche, lorsque vous rencontrez l'instance retournée parFunction.identity()
lors du débogage d'une opération, vous ne saurez pas qui a appelé cette méthode et transmis l'instance à l'opération.la source
x -> x
trame. Suggérez-vous de définir le point d'arrêt à cette lambda? Habituellement, il n'est pas si facile de mettre le point d'arrêt dans la lambda à expression simple (au moins dans Eclipse) ...Function.identity()
cette information est perdue. Ensuite, la chaîne d'appel peut aider dans des cas simples, mais pensez, par exemple, à une évaluation multithread où l'initiateur d'origine n'est pas dans la trace de la pile…new
.new Foo(…)
garanties pour créer une nouvelle instance du type exactFoo
, alors que,Foo.getInstance(…)
peut renvoyer une instance existante de (un sous-type de)Foo
…Dans votre exemple, il n'y a pas de grande différence entre
str -> str
etFunction.identity()
puisque c'est en interne simplementt->t
.Mais parfois, nous ne pouvons pas utiliser
Function.identity
parce que nous ne pouvons pas utiliser unFunction
. Jetez un œil ici:cela compilera bien
mais si vous essayez de compiler
vous obtiendrez une erreur de compilation depuis
mapToInt
attendToIntFunction
, ce qui n'est pas lié àFunction
. N'aToIntFunction
pas non plus deidentity()
méthode.la source
i -> i
parFunction.identity()
entraînera une erreur de compilation.mapToInt(Integer::intValue)
.mapToInt(i -> i)
c'est la simplification demapToInt( (Integer i) -> i.intValue())
. Utilisez la version que vous pensez la plus claire, pour moi, celamapToInt(i -> i)
montre mieux les intentions de ce code.i -> i
ressemble à une fonction d'identité, ce qui n'est pas le cas dans ce cas.i -> i
car mon objectif est de mapper Integer à int (ce quimapToInt
suggère très bien) de ne pas appeler explicitement laintValue()
méthode. La manière dont cette cartographie sera réalisée n'est pas vraiment importante. Donc, acceptons simplement d'être en désaccord, mais merci d'avoir signalé une éventuelle différence de performances, je devrai y regarder un jour de plus près.De la source JDK :
Donc, non, tant qu'il est syntaxiquement correct.
la source
t->t
dans le code source peut créer un objet et l'implémentation deFunction.identity()
est une occurrence. Ainsi, tous les sites d'appel invoquantidentity()
partageront cet objet tandis que tous les sites utilisant explicitement l'expression lambdat->t
créeront leur propre objet. La méthodeFunction.identity()
n'a rien de spécial, chaque fois que vous créez une méthode d'usine encapsulant une expression lambda couramment utilisée et appelez cette méthode au lieu de répéter l'expression lambda, vous pouvez économiser de la mémoire, compte tenu de l'implémentation actuelle .t->t
objet à chaque appel de la méthode et recycle le même chaque fois que la méthode est appelée?invokedynamic
instruction qui est liée lors de sa première exécution en exécutant une méthode dite de bootstrap, qui dans le cas des expressions lambda se trouve dans leLambdaMetafactory
. Cette implémentation décide de renvoyer un handle vers un constructeur, une méthode d'usine ou un code renvoyant toujours le même objet. Il peut également décider de renvoyer un lien vers un descripteur déjà existant (ce qui ne se produit pas actuellement).