A SayHello
é uma interface Single Abstract Method que possui um método que pega uma string e retorna nulo. Isso é análogo a um consumidor. Você está apenas fornecendo uma implementação desse método na forma de um consumidor semelhante à seguinte implementação de classe interna anônima.
SayHello sh = new SayHello() {
@Override
public void speak(String message) {
System.out.println("Hello " + message);
}
};
names.forEach(n -> sh.speak(n));
Felizmente, sua interface possui um método único para que o sistema de tipos (mais formalmente, o algoritmo de resolução de tipos) possa inferir seu tipo como SayHello
. Mas se tivesse dois ou mais métodos, isso não seria possível.
No entanto, uma abordagem muito melhor é declarar o consumidor antes do loop for e usá-lo como mostrado abaixo. Declarar a implementação para cada iteração cria mais objetos do que o necessário e parece contra-intuitivo para mim. Aqui está a versão aprimorada usando referências de método em vez de lambda. A referência do método limitado usada aqui chama o método relevante na hello
instância declarada acima.
SayHello hello = message -> System.out.println("Hello " + message);
names.forEach(hello::speak);
Atualizar
Dado que para lambdas sem estado que não captura nada do seu escopo lexical apenas uma vez que a instância é criada, ambas as abordagens simplesmente criam uma instância da SayHello
e não há nenhum ganho após a abordagem sugerida. No entanto, este parece ser um detalhe de implementação e eu não o conhecia até agora. Portanto, uma abordagem muito melhor é passar o consumidor para o seu forEach, conforme sugerido no comentário abaixo. Observe também que todas essas abordagens criam apenas uma instância da sua SayHello
interface, enquanto a última é mais sucinta. Aqui está como fica.
names.forEach(message -> System.out.println("Hello " + message));
Esta resposta lhe dará mais informações sobre isso. Aqui está a seção relevante do JLS §15.27.4 : Avaliação em tempo de execução de expressões lambda
Essas regras visam oferecer flexibilidade às implementações da linguagem de programação Java, na medida em que:
- Um novo objeto não precisa ser alocado em todas as avaliações.
De fato, inicialmente pensei que toda avaliação cria uma nova instância, o que está errado. @ Holger obrigado por apontar isso, boa captura.
Consumer<String>
vez de tersayHello
interface.SayHello hello = message -> System.out.println("Hello " + message);
, haverá apenas umaSayHello
instância. Dado que mesmo um objeto é obsoleto aqui de qualquer maneira, como o mesmo é possível vianames.forEach(n -> System.out.println("Hello " + n))
, não há melhorias na sua "versão aprimorada".A assinatura do foreach é semelhante à abaixo:
Leva em um objeto de consumidor. A interface do consumidor é uma interface funcional (uma interface com um único método abstrato). Ele aceita uma entrada e não retorna nenhum resultado.
Aqui está a definição:
Portanto, qualquer implementação, a do seu caso, pode ser escrita de duas maneiras:
OU
Da mesma forma, o código da classe SayHello pode ser escrito como
OU
Portanto,
names.foreach
primeiro internamente chama o método consumer.accept e executa sua implementação lambda / anônima, que cria e chama a implementação SayHello lambda e assim por diante. Usando a expressão lambda, o código é muito compacto e claro. A única coisa que você precisa entender é como está funcionando, ou seja, no seu caso, ele estava usando a interface do Consumidor.Fluxo final:
foreach -> consumer.accept -> sayhello.speak
fonte