El SayHello
es una interfaz de método abstracto único que tiene un método que toma una cadena y devuelve vacío. Esto es análogo a un consumidor. Solo está proporcionando una implementación de ese método en forma de consumidor que es similar a la siguiente implementación anónima de clase interna.
SayHello sh = new SayHello() {
@Override
public void speak(String message) {
System.out.println("Hello " + message);
}
};
names.forEach(n -> sh.speak(n));
Afortunadamente, su interfaz tiene un método único para que el sistema de tipos (más formalmente, el algoritmo de resolución de tipos) pueda inferir su tipo como SayHello
. Pero si tuviera 2 o más métodos, esto no sería posible.
Sin embargo, un enfoque mucho mejor es declarar al consumidor antes del ciclo for y usarlo como se muestra a continuación. Declarar la implementación para cada iteración crea más objetos de los necesarios y me parece contra-intuitivo. Aquí está la versión mejorada usando referencias de métodos en lugar de lambda. La referencia del método acotado utilizada aquí llama al método relevante en la hello
instancia declarada anteriormente.
SayHello hello = message -> System.out.println("Hello " + message);
names.forEach(hello::speak);
Actualizar
Dado que para lambdas apátridas que no capturan nada de su alcance léxico solo una vez que se creará una instancia, ambos enfoques simplemente crean una instancia de la SayHello
, y no hay ninguna ganancia siguiendo el enfoque sugerido. Sin embargo, esto parece ser un detalle de implementación y no lo sabía hasta ahora. Por lo tanto, un enfoque mucho mejor es simplemente pasar a un consumidor a su ForEach como se sugiere en el comentario a continuación. También tenga en cuenta que todos estos enfoques crean solo una instancia de su SayHello
interfaz, mientras que la última es más sucinta. Así es como se ve.
names.forEach(message -> System.out.println("Hello " + message));
Esta respuesta te dará más información sobre eso. Aquí está la sección relevante de JLS §15.27.4 : Evaluación en tiempo de ejecución de expresiones lambda
Estas reglas están destinadas a ofrecer flexibilidad a las implementaciones del lenguaje de programación Java, en el sentido de que:
- No es necesario asignar un nuevo objeto en cada evaluación.
De hecho, inicialmente pensé que cada evaluación crea una nueva instancia, lo cual está mal. @ Holger, gracias por señalarlo, buena captura.
Consumer<String>
lugar de tener unasayHello
interfaz.SayHello hello = message -> System.out.println("Hello " + message);
, solo habrá unaSayHello
instancia. Dado que incluso ese objeto es obsoleto aquí de todos modos, ya que el mismo se puede lograr a través denames.forEach(n -> System.out.println("Hello " + n))
, no hay mejora en su "versión mejorada".La firma de foreach es algo así como a continuación:
Toma en un objeto de consumidor. La interfaz de consumidor es una interfaz funcional (una interfaz con un único método abstracto). Acepta una entrada y no devuelve ningún resultado.
Aquí está la definición:
Por lo tanto, cualquier implementación, la de su caso, se puede escribir de dos maneras:
O
Del mismo modo, el código para la clase SayHello se puede escribir como
O
Entonces,
names.foreach
internamente, primero llama al método consumer.accept y ejecuta su implementación lambda / anónima que crea y llama a la implementación SayHello lambda y así sucesivamente. Usando la expresión lambda, el código es muy compacto y claro. Lo único que necesita para comprender cómo funciona es decir, en su caso, estaba utilizando la interfaz de consumidor.Entonces flujo final:
foreach -> consumer.accept -> sayhello.speak
fuente