parameter - ¿Por qué no se puede asignar a la palabra clave var en Java una expresión lambda?
lambda expression java (6)
Como varias personas ya han mencionado, ¿qué tipo debe inferir
var
y por qué debería hacerlo?
La declaración:
var predicateVar = apple -> apple.getColor().equals("red");
es ambiguo y no hay una razón válida por la cual el compilador debe elegir la
Function<Apple, Boolean>
sobre
Predicate<Apple>
o viceversa, suponiendo que el identificador de
apple
en el lambda representa una instancia de
Apple
.
Otra razón es que una lambda en sí misma no tiene un tipo hablable, por lo tanto, no hay forma de que el compilador pueda inferirla.
Además,
"si esto fuera posible"
imagine la sobrecarga ya que el compilador tendría que pasar por todas las interfaces funcionales y determinar qué interfaz funcional es la más apropiada cada vez que asigna una lambda a una variable
var
.
Está permitido asignar
var
en Java 10 con una cadena como:
var foo = "boo";
Si bien no está permitido asignarlo con una expresión lambda como:
var predicateVar = apple -> apple.getColor().equals("red");
¿Por qué no puede inferir un tipo de referencia lambda o método cuando puede inferir el resto como
String
,
ArrayList
, clase de usuario, etc.?
Del JEP de inferencia de tipo variable local :
El proceso de inferencia, sustancialmente, solo le da a la variable el tipo de su expresión inicializadora. Algunas sutilezas:
- El inicializador no tiene un tipo de objetivo (porque todavía no lo hemos inferido). Las expresiones poli que requieren ese tipo, como lambdas , referencias de métodos e inicializadores de matriz, desencadenarán un error.
Debido a que una expresión lambda por sí sola no tiene un tipo, no se puede inferir para
var
.
... Del mismo modo, se podría establecer una regla predeterminada.
Claro, puede encontrar una manera de evitar esta limitación. El motivo por el cual los desarrolladores tomaron la decisión de no hacerlo depende realmente de la especulación, a menos que alguien que haya sido parte de la toma de decisiones pueda responder aquí. Si está interesado de todos modos, puede preguntar al respecto en una de las listas de correo de openjdk: http://mail.openjdk.java.net/mailman/listinfo
Si tuviera que adivinar, probablemente no querían vincular la inferencia lambda en el contexto de
var
a un conjunto específico de tipos de interfaz funcional, lo que excluiría cualquier tipo de interfaz funcional de terceros.
Una mejor solución sería inferir un tipo de función genérico (es decir,
(Apple) -> boolean
) que puede convertirse en un tipo de interfaz funcional compatible.
Pero la JVM no tiene tales tipos de funciones, y la decisión de no implementarlas ya se tomó durante el proyecto que creó las expresiones lambda.
Nuevamente, si está interesado en razones concretas, pregunte a los desarrolladores.
Esto no tiene nada que ver con la
var
.
Tiene que ver con si una lambda tiene un
tipo independiente
.
La forma en que funciona
var
es que calcula el tipo independiente del inicializador en el RHS, e infiere eso.
Desde su introducción en Java 8, las expresiones lambda y las referencias de métodos no tienen un tipo independiente: requieren un tipo de destino , que debe ser una interfaz funcional.
Si intentas:
Object o = (String s) -> s.length();
también obtiene un error de tipo, porque el compilador no tiene idea de a qué interfaz funcional tiene la intención de convertir el lambda.
Pedir inferencia con
var
solo lo hace más difícil, pero dado que la pregunta más fácil no se puede responder, la más difícil tampoco.
Tenga en cuenta que podría proporcionar un tipo de objetivo por otros medios (como un yeso) y luego funcionaría:
var x = (Predicate<String>) s -> s.isEmpty();
porque ahora el RHS tiene un tipo independiente.
Pero es mejor proporcionar el tipo de destino al dar
x
un tipo de manifiesto.
Para responder esto, tenemos que entrar en detalles y comprender qué es una lambda y cómo funciona.
Primero debemos entender qué es una lambda:
Una expresión lambda siempre implementa una interfaz funcional, de modo que cuando tiene que proporcionar una interfaz funcional como
Runnable
, en lugar de tener que crear una clase completamente nueva que implemente la interfaz, puede usar la sintaxis lambda para crear un método que funcione La interfaz requiere.
Sin embargo, tenga en cuenta que el lambda todavía tiene el tipo de interfaz funcional que está implementando.
Con eso en mente, llevemos esto un paso más allá:
Esto funciona muy bien, como en el caso de Runnable, solo puedo crear un nuevo hilo como este
new Thread(()->{//put code to run here});
en lugar de crear un objeto completamente nuevo para implementar la interfaz funcional.
Esto funciona ya que el compilador sabe que
Thread()
toma un objeto de tipo Runnable, por lo que sabe de qué tipo debe ser la expresión lambda.
Sin embargo, en el caso de asignar una lambda a una variable local, el compilador no tiene idea de qué interfaz funcional está implementando esta lambda, por lo que no puede inferir qué tipo de
var
debería ser.
Como tal vez está implementando una interfaz funcional que creó el usuario o tal vez es la interfaz
runnable
, simplemente no hay forma de saberlo.
Es por eso que las lambdas no funcionan con la palabra clave var.
Para todos los que dicen que esto es imposible, no deseado o no deseado, solo quiero señalar que Scala puede inferir el tipo de lambda especificando solo el tipo de argumento:
val predicateVar = (apple: Apple) => apple.getColor().equals("red")
Y en Haskell, debido a que
getColor
sería una función independiente no asociada a un objeto, y debido a que realiza una inferencia completa de Hindley-Milner, no necesita especificar ni siquiera el tipo de argumento:
predicateVar = /apple -> getColor apple == "red"
Esto es extraordinariamente útil, porque no son los tipos simples los que molestan a los programadores especificar explícitamente, son los más complejos.
En otras palabras, no es una característica en Java 10. Es una limitación de su implementación y opciones de diseño anteriores.
Porque eso no es una característica:
Este tratamiento se restringiría a variables locales con inicializadores, índices en el bucle for mejorado y locales declarados en un bucle for tradicional; no estaría disponible para formales de método, formales de constructor, tipos de retorno de método, campos, formales de captura o cualquier otro tipo de declaración de variable.