Il est autorisé d'assigner var
in Java 10 avec une chaîne comme celle-ci:
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 type de référence lambda ou de méthode lorsqu'il peut déduire le reste comme String
, ArrayList
, une classe d'utilisateurs, etc.?
À partir du PEC d'inférence de type de variable locale :
Le processus d'inférence, en substance, donne simplement à la variable le type de son expression d'initialiseur. Quelques subtilités:
- L'initialiseur n'a pas de type de cible (car nous ne l'avons pas encore inféré). Les expressions poly qui requièrent un tel type, telles que lambdas , les références de méthode et les initialiseurs de tableaux, déclencheront une erreur.
Comme une expression lambda en elle-même n’a pas de type, elle ne peut pas être déduite pour var
.
De même, une règle par défaut pourrait être définie.
Bien sûr, vous pouvez trouver un moyen de contourner cette limitation. Pourquoi les développeurs ont-ils décidé de ne pas le faire, cela relève vraiment de la spéculation, à moins que quelqu'un qui a pris part à la prise de décision puisse répondre ici. Si cela vous intéresse, vous pouvez le demander sur l’une des listes de diffusion openjdk: http://mail.openjdk.Java.net/mailman/listinfo
Si je devais deviner, ils ne voulaient probablement pas lier l'inférence lambda dans le contexte de var
à un ensemble spécifique de types d'interface fonctionnelle, ce qui exclurait tout type d'interface fonctionnelle tierce. Une meilleure solution consisterait à déduire un type de fonction générique (c'est-à-dire (Apple) -> boolean
) qui peut ensuite être converti en un type d’interface fonctionnelle compatible. Mais la machine virtuelle Java ne dispose pas de tels types de fonctions et la décision de ne pas les implémenter a déjà été prise lors du projet de création d'expressions lambda. Encore une fois si vous êtes intéressé par des raisons concrètes, demandez aux développeurs.
Cela n'a rien à voir avec var
. Cela dépend de la question de savoir si un lambda a un type autonome. Le fonctionnement de var
est qu'il calcule le type autonome de l'initialiseur sur le RHS et en déduit.
Depuis leur introduction dans Java 8, les expressions lambda et les références de méthodes n'ont pas de type autonome. Elles nécessitent un type cible, qui doit être une interface fonctionnelle.
Si tu essayes:
Object o = (String s) -> s.length();
vous obtenez également une erreur de type, car le compilateur n'a aucune idée de l'interface fonctionnelle vers laquelle vous souhaitez convertir le lambda.
Demander l'inférence avec var
rend la tâche plus difficile, mais comme il est impossible de répondre à la question la plus facile, le plus difficile ne l'est pas non plus.
Notez que vous pouvez fournir un type de cible par d'autres moyens (tels qu'un transtypage), puis cela fonctionnera:
var x = (Predicate<String>) s -> s.isEmpty();
parce que maintenant le RHS a un type autonome. Mais il est préférable de fournir le type de cible en donnant à x
un type de manifeste.
Pour tous ceux qui disent que cela est impossible, indésirable ou indésirable, je tiens simplement à souligner que Scala peut déduire le type de lambda en spécifiant uniquement le type d'argument:
val predicateVar = (Apple: Apple) => Apple.getColor().equals("red")
Et en Haskell, étant donné que getColor
serait une fonction autonome non attachée à un objet, et comme elle permet une inférence complète de Hindley-Milner, vous n'avez même pas besoin de spécifier le type d'argument:
predicateVar = \Apple -> getColor Apple == "red"
Ceci est extrêmement pratique, car ce ne sont pas les types simples qui sont ennuyeux à spécifier explicitement par les programmeurs, ce sont les plus complexes.
En d’autres termes, ce n’est pas une fonctionnalité de Java 10.). C’est une limitation de leur implémentation et de leurs choix de conception antérieurs.
Comme plusieurs personnes l'ont déjà mentionné, quel type devrait var
déduire et pourquoi?
La déclaration:
var predicateVar = Apple -> Apple.getColor().equals("red");
est ambigu et il n'y a aucune raison valable pour laquelle le compilateur devrait choisir Function<Apple, Boolean>
plus de Predicate<Apple>
ou vice-versa en supposant que l'identifiant Apple
du lambda représente un Apple
isntance.
Une autre raison est qu’un lambda en lui-même n’a pas de type parlable et qu’il n’ya donc aucun moyen pour le compilateur de le déduire.
En outre, "si cela était possible" imaginez la surcharge car le compilateur devrait passer par toutes les interfaces fonctionnelles et déterminer quelle interface fonctionnelle est la plus appropriée chaque fois que vous affectez un lambda à un var
variable.
Pour répondre à cette question, nous devons entrer dans les détails et comprendre ce qu'est un lambda et son fonctionnement.
D'abord, nous devrions comprendre ce qu'est un lambda:
Une expression lambda implémente toujours une interface fonctionnelle. Ainsi, lorsque vous devez fournir une interface fonctionnelle telle que Runnable
, au lieu de devoir créer une nouvelle classe qui implémente l'interface, vous pouvez simplement utiliser la syntaxe lambda pour créer. une méthode requise par l'interface fonctionnelle. Gardez toutefois à l'esprit que le lambda a toujours le type de l'interface fonctionnelle qu'il implémente.
Gardons cela à l’esprit:
Cela fonctionne très bien car dans le cas de Runnable, je peux simplement créer un nouveau thread comme celui-ci new Thread(()->{//put code to run here});
au lieu de créer un nouvel objet complet pour implémenter l'interface fonctionnelle. Cela fonctionne car le compilateur sait que Thread()
prend un objet de type Runnable, de sorte qu'il sait quel type doit être l'expression lambda.
Cependant, dans le cas de l'affectation d'un lambda à une variable locale, le compilateur n'a aucune idée de l'interface fonctionnelle implémentée par ce lambda et ne peut donc pas en déduire le type var
. Puisqu'il s'agit peut-être d'une interface fonctionnelle créée par l'utilisateur ou de l'interface runnable
, il n'y a aucun moyen de le savoir.
C'est pourquoi lambdas ne fonctionne pas avec le mot clé var.
Parce que c'est une non-fonctionnalité:
Ce traitement serait limité aux variables locales avec initialiseurs, aux index de la boucle for améliorée et aux locales déclarées dans une boucle for traditionnelle; il ne serait pas disponible pour les formals de méthode, les constructeurs, les types de retour de méthode, les champs, les formals de capture ou tout autre type de déclaration de variable.