78 votes

Pourquoi le mot-clé var en Java ne peut-il pas recevoir une expression lambda?

Il est autorisé d'assigner var en Java 10 avec une chaîne comme:

 var foo = "boo";
 

Bien qu'il ne soit pas autorisé de l'assigner avec une expression lambda telle que:

 var predicateVar = apple -> apple.getColor().equals("red");
 

Pourquoi ne peut-il pas déduire un lambda ou un type de référence de méthode alors qu'il peut déduire le reste comme String , ArrayList , classe d'utilisateurs, etc.?

75voto

Brian Goetz Points 6062

Cela n'a rien à voir avec var. Il a à voir avec un lambda a un autonome type. La façon dont var fonctionne est qu'il calcule la version autonome de type de l'initialiseur sur les RHS, et en déduit que.

Depuis leur introduction en Java 8, les expressions lambda et de la méthode références ont pas autonome type: ils nécessitent un type de cible, qui doit être une interface fonctionnelle.

Si vous essayez de:

Object o = (String s) -> s.length();

vous bénéficiez également d'un type d'erreur, parce que le compilateur n'a aucune idée de ce que l'interface fonctionnelle vous avez l'intention de convertir le lambda.

Demander de l'inférence avec var rend simplement plus difficile, mais depuis la plus facile question ne peut être répondu, le plus difficile, on ne peut pas non plus.

Notez que vous pouvez fournir un type de cible par d'autres moyens (tels que la fonte) et puis ça devrait fonctionner:

var x = (Predicate<String>) s -> s.isEmpty();

parce que maintenant, le membre de droite est autonome type. Mais vous êtes mieux de fournir le type de cible en donnant des x manifeste type.

64voto

Jorn Vernee Points 18630

De la Locale de la Variable que l'Inférence de Type PEC:

Le processus d'inférence, de façon substantielle, donne juste la variable du type de son initialiseur d'expression. Quelques subtilités:

  • L'initialiseur, n'a pas de type de cible (parce que nous n'avons pas déduit encore). Poly expressions qui ont besoin d'un tel type, comme les lambdas, la méthode des références, et les initialiseurs de tableau, va déclencher une erreur.

Parce qu'une lambda expression en elle-même n'ont pas de type, il ne peut pas être déduit pour var.


... De la même façon, une règle par défaut a pu être défini.

Bien sûr, vous pouvez venir avec un moyen de contourner cette limitation. Pourquoi les développeurs ont fait de la décision de ne pas le faire c'est vraiment de la spéculation, à moins que quelqu'un qui a participé à la prise de décision peut répondre ici. Si vous êtes intéressé de toute façon, vous pourriez demander à ce sujet sur l'un des openjdk listes de diffusion: http://mail.openjdk.java.net/mailman/listinfo

Si je devais deviner, ils probablement ne voulez pas de cravate lambda inférence dans le cadre de l' var à un ensemble spécifique d'interface fonctionnelle types, ce qui permettrait d'exclure tout tiers fonctionnelle types d'interface. Une meilleure solution serait d'en déduire une fonction générique type ( (Apple) -> boolean) qui peut être converti en compatible fonctionnelle type d'interface. Mais la JVM n'a pas de ces types de fonction, et la décision de ne pas les mettre en œuvre a déjà été fait au cours du projet, qui a créé les expressions lambda. Si vous êtes intéressé par des raisons concrètes, demandez aux devs.

35voto

Karl Bielefeldt Points 15469

À tous ceux qui disent que c'est impossible, non désirés ou non désirés, je veux juste souligner que la Scala pouvons en déduire que le lambda de type spécifiant uniquement le type d'argument:

val predicateVar = (apple: Apple) => apple.getColor().equals("red")

Et en Haskell, car getColor serait une fonction autonome qui n'est pas attachée à un objet, et parce qu'il n'plein Hindley-Milner inférence, vous ne devez pas spécifier de même que le type d'argument:

predicateVar = \apple -> getColor apple == "red"

C'est extrêmement pratique, car ce ne sont pas les types simples qui sont ennuyeux pour les programmeurs de spécifier explicitement, c'est le plus complexe.

En d'autres termes, ce n'est pas une fonction en Java 10. C'est une limitation de leur mise en œuvre et le précédent choix de conception.

5voto

Aomine Points 42709

Comme plusieurs personnes l'ont déjà mentionné, ce type devrait var inférer et pourquoi devrait-il?

La déclaration:

var predicateVar = apple -> apple.getColor().equals("red");

est ambigu et il n'y a aucune raison valable pourquoi le compilateur devrait reprendre Function<Apple, Boolean> sur Predicate<Apple> , ou vice versa, en supposant que l' apple identifiant dans le lambda représente un Apple isntance.

Une autre raison est qu'un lambda dans son propre n'ont pas de speakable type donc il n'y a aucun moyen pour le compilateur de déduire cela.

Aussi, "si c'était possible" imaginer la surcharge comme le compilateur aurait à passer par toutes les interfaces fonctionnelles et de déterminer quelle interface fonctionnelle est la plus appropriée à chaque fois que vous affectez un lambda à une var variable.

5voto

SteelToe Points 1310

Pour répondre à cela, nous devons aller dans les détails et de comprendre ce qu'est un lambda est et comment il fonctionne.

Il faut d'abord comprendre ce qu'est un lambda est:

Une expression lambda toujours implémente une interface fonctionnelle, de sorte que lorsque vous avez à fournir une interface fonctionnelle comme Runnable, au lieu de devoir créer une nouvelle classe qui implémente l'interface, vous pouvez simplement utiliser le lambda de la syntaxe pour créer une méthode de l'interface fonctionnelle exige. Gardez à l'esprit que le lambda a encore le type de l'interface fonctionnelle qu'elle est mise en œuvre.

Avec cela à l'esprit, permet de faire un pas de plus:

Cela fonctionne très bien, comme dans le cas de l'Exécutable, je peux juste créer un nouveau thread comme celui-ci new Thread(()->{//put code to run here}); au lieu de créer un tout nouvel objet de mettre en œuvre l'interface fonctionnelle. Cela fonctionne puisque le compilateur sait qu' Thread() prend un objet de type Runnable, afin qu'il sache quel type de l'expression lambda doit être.

Toutefois, en cas d'attribution d'un lambda à une variable locale, le compilateur n'a aucune idée de ce qu'interface fonctionnelle cette lambda est la mise en œuvre de sorte qu'il ne peut pas en déduire ce type var devraient l'être. Car c'est peut-être la mise en œuvre d'une interface fonctionnelle créés par l'utilisateur ou peut-être l' runnable interface, il n'y a simplement aucun moyen de savoir.

C'est pourquoi elles ne fonctionnent pas avec le mot-clé var.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X