JavaRush /Blogue Java /Random-PT /Expressões lambda com exemplos

Expressões lambda com exemplos

Publicado no grupo Random-PT
Java é inicialmente uma linguagem completamente orientada a objetos. Com exceção dos tipos primitivos, tudo em Java é um objeto. Até mesmo arrays são objetos. As instâncias de cada classe são objetos. Não existe uma única possibilidade de definir uma função separadamente (fora de uma classe - aprox. trad. ). E não há como passar um método como argumento ou retornar o corpo de um método como resultado de outro método. É assim. Mas isso foi antes do Java 8. Expressões lambda com exemplos - 1Desde os tempos do bom e velho Swing, era necessário escrever classes anônimas quando era necessário passar alguma funcionalidade para algum método. Por exemplo, a adição de um manipulador de eventos era assim:
someObject.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {

                //Event listener implementation goes here...

            }
        });
Aqui queremos adicionar algum código ao ouvinte de eventos do mouse. Definimos uma classe anônima MouseAdaptere imediatamente criamos um objeto a partir dela. Dessa forma, passamos funcionalidades adicionais para o addMouseListener. Resumindo, não é fácil passar um método simples (funcionalidade) em Java por meio de argumentos. Essa limitação forçou os desenvolvedores Java 8 a adicionar um recurso como expressões Lambda à especificação da linguagem.

Por que Java precisa de expressões Lambda?

Desde o início, a linguagem Java não evoluiu muito, exceto em coisas como Anotações, Genéricos, etc. Em primeiro lugar, Java sempre permaneceu orientado a objetos. Depois de trabalhar com linguagens funcionais como JavaScript, pode-se entender como Java é estritamente orientado a objetos e fortemente tipado. Funções não são necessárias em Java. Por si só, eles não podem ser encontrados no mundo Java. Nas linguagens de programação funcionais, as funções vêm à tona. Eles existem por conta própria. Você pode atribuí-los a variáveis ​​e passá-los através de argumentos para outras funções. JavaScript é um dos melhores exemplos de linguagens de programação funcionais. Você pode encontrar bons artigos na Internet que detalham os benefícios do JavaScript como linguagem funcional. As linguagens funcionais possuem ferramentas poderosas, como o Closure, que oferecem uma série de vantagens em relação aos métodos tradicionais de escrita de aplicativos. Um encerramento é uma função com um ambiente anexado - uma tabela que armazena referências a todas as variáveis ​​não locais da função. Em Java, os encerramentos podem ser simulados por meio de expressões Lambda. É claro que existem diferenças entre encerramentos e expressões lambda, e não pequenas, mas as expressões lambda são uma boa alternativa aos encerramentos. Em seu blog sarcástico e engraçado, Steve Yegge descreve como o mundo de Java está estritamente ligado a substantivos (entidades, objetos - aproximadamente. tradução ). Se você ainda não leu o blog dele, recomendo. Ele descreve de uma forma engraçada e interessante o motivo exato pelo qual as expressões Lambda foram adicionadas ao Java. As expressões lambda trazem funcionalidades ao Java que estavam ausentes há tanto tempo. As expressões lambda trazem funcionalidade à linguagem, assim como os objetos. Embora isso não seja 100% verdade, você pode ver que as expressões Lambda, embora não sejam encerramentos, fornecem recursos semelhantes. Em uma linguagem funcional, as expressões lambda são funções; mas em Java, as expressões lambda são representadas por objetos e devem ser associadas a um tipo de objeto específico denominado interface funcional. A seguir veremos o que é. O artigo de Mario Fusco “Por que precisamos da expressão Lambda em Java” descreve em detalhes por que todas as linguagens modernas precisam de recursos de fechamento.

Introdução às Expressões Lambda

Expressões lambda são funções anônimas (pode não ser uma definição 100% correta para Java, mas traz alguma clareza). Simplificando, este é um método sem declaração, ou seja, sem modificadores de acesso, retornando valor e nome. Resumindo, eles permitem escrever um método e usá-lo imediatamente. É especialmente útil no caso de uma chamada de método única, porque reduz o tempo necessário para declarar e escrever um método sem precisar criar uma classe. Expressões lambda em Java normalmente têm a seguinte sintaxe (аргументы) -> (тело). Por exemplo:
(арг1, арг2...) -> { тело }

(тип1 арг1, тип2 арг2...) -> { тело }
Abaixo estão alguns exemplos de expressões Lambda reais:
(int a, int b) -> {  return a + b; }

() -> System.out.println("Hello World");

(String s) -> { System.out.println(s); }

() -> 42

() -> { return 3.1415 };

Estrutura das Expressões Lambda

Vamos estudar a estrutura das expressões lambda:
  • As expressões lambda podem ter 0 ou mais parâmetros de entrada.
  • O tipo de parâmetros pode ser especificado explicitamente ou obtido no contexto. Por exemplo ( int a) pode ser escrito assim ( a)
  • Os parâmetros são colocados entre parênteses e separados por vírgulas. Por exemplo ( a, b) ou ( int a, int b) ou ( String a, int b, float c)
  • Se não houver parâmetros, será necessário usar parênteses vazios. Por exemplo() -> 42
  • Quando houver apenas um parâmetro, se o tipo não for especificado explicitamente, os parênteses poderão ser omitidos. Exemplo:a -> return a*a
  • O corpo de uma expressão Lambda pode conter 0 ou mais expressões.
  • Se o corpo consistir em uma única instrução, ele não poderá ser colocado entre chaves e o valor de retorno poderá ser especificado sem a palavra-chave return.
  • Caso contrário, as chaves são obrigatórias (bloco de código) e o valor de retorno deve ser especificado no final usando uma palavra-chave return(caso contrário, o tipo de retorno será void).

O que é uma interface funcional

Em Java, as interfaces Marker são interfaces sem declaração de métodos ou campos. Em outras palavras, as interfaces de token são interfaces vazias. Da mesma forma, Interfaces Funcionais são interfaces com apenas um método abstrato declarado. java.lang.Runnableé um exemplo de interface funcional. Ele declara apenas um método void run(). Há também uma interface ActionListener- também funcional. Anteriormente, tínhamos que usar classes anônimas para criar objetos que implementassem uma interface funcional. Com as expressões Lambda, tudo ficou mais simples. Cada expressão lambda pode ser implicitamente vinculada a alguma interface funcional. Por exemplo, você pode criar uma referência a Runnableuma interface, conforme mostrado no exemplo a seguir:
Runnable r = () -> System.out.println("hello world");
Este tipo de conversão é sempre feito implicitamente quando não especificamos uma interface funcional:
new Thread(
    () -> System.out.println("hello world")
).start();
No exemplo acima, o compilador cria automaticamente uma expressão lambda como uma implementação da Runnableinterface do construtor da classe Thread:. public Thread(Runnable r) { }Aqui estão alguns exemplos de expressões lambda e interfaces funcionais correspondentes:
Consumer<Integer> c = (int x) -> { System.out.println(x) };

BiConsumer<Integer, String> b = (Integer x, String y) -> System.out.println(x + " : " + y);

Predicate<String> p = (String s) -> { s == null };
A anotação @FunctionalInterfaceadicionada no Java 8 de acordo com a Especificação da Linguagem Java verifica se a interface declarada está funcional. Além disso, o Java 8 inclui diversas interfaces funcionais prontas para uso com expressões Lambda. @FunctionalInterfacegerará um erro de compilação se a interface declarada não estiver funcional. A seguir está um exemplo de definição de uma interface funcional:
@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();

}
Como sugere a definição, uma interface funcional pode ter apenas um método abstrato. Se você tentar adicionar outro método abstrato, receberá um erro de compilação. Exemplo:
@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();

    public void doSomeMoreWork();

}
Erro
Unexpected @FunctionalInterface annotation
    @FunctionalInterface ^ WorkerInterface is not a functional interface multiple
    non-overriding abstract methods found in interface WorkerInterface 1 error
После определения функционального интерфейса, мы можем его использовать и получать все преимущества Lambda-выражений. Пример:// defining a functional interface
@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();

}
public class WorkerInterfaceTest {

    public static void execute(WorkerInterface worker) {
        worker.doSomeWork();
    }

    public static void main(String [] args) {

      // calling the doSomeWork method via an anonymous class
      // (classic)
      execute(new WorkerInterface() {
            @Override
            public void doSomeWork() {
               System.out.println("Worker called via an anonymous class");
            }
        });

      // calling the doSomeWork method via Lambda expressions
      // (Java 8 new)
      execute( () -> System.out.println("Worker called via Lambda") );
    }

}
Conclusão:
Worker вызван через анонимный класс
Worker вызван через Lambda
Aqui definimos nossa própria interface funcional e usamos uma expressão lambda. O método execute()é capaz de aceitar expressões lambda como argumento.

Exemplos de expressões lambda

A melhor maneira de entender as expressões Lambda é observar alguns exemplos: Um fluxo Threadpode ser inicializado de duas maneiras:
// Old way:
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello from thread");
    }
}).start();
// New way:
new Thread(
    () -> System.out.println("Hello from thread")
).start();
O gerenciamento de eventos em Java 8 também pode ser feito por meio de expressões Lambda. A seguir estão duas maneiras de adicionar um manipulador de eventos ActionListenera um componente de UI:
// Old way:
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Button pressed. Old way!");
    }
});
// New way:
button.addActionListener( (e) -> {
        System.out.println("Button pressed. Lambda!");
});
Um exemplo simples de exibição de todos os elementos de um determinado array. Observe que há mais de uma maneira de usar uma expressão lambda. Abaixo, criamos uma expressão lambda da maneira usual usando a sintaxe de seta e também usamos o operador de dois pontos duplos (::), que em Java 8 converte um método regular em uma expressão lambda:
// Old way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for(Integer n: list) {
    System.out.println(n);
}
// New way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> System.out.println(n));
// New way using double colon operator ::
list.forEach(System.out::println);
No exemplo a seguir, usamos uma interface funcional Predicatepara criar um teste e imprimir os itens que passam nesse teste. Dessa forma, você pode colocar lógica em expressões lambda e fazer coisas com base nela.
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class Main {

    public static void main(String [] a)  {

        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);

        System.out.print("Outputs all numbers: ");
        evaluate(list, (n)->true);

        System.out.print("Does not output any number: ");
        evaluate(list, (n)->false);

        System.out.print("Output even numbers: ");
        evaluate(list, (n)-> n%2 == 0 );

        System.out.print("Output odd numbers: ");
        evaluate(list, (n)-> n%2 == 1 );

        System.out.print("Output numbers greater than 5: ");
        evaluate(list, (n)-> n > 5 );

    }

    public static void evaluate(List<Integer> list, Predicate<Integer> predicate) {
        for(Integer n: list)  {
            if(predicate.test(n)) {
                System.out.print(n + " ");
            }
        }
        System.out.println();
    }

}
Conclusão:
Выводит все числа: 1 2 3 4 5 6 7
Не выводит ни одного числа:
Вывод четных чисел: 2 4 6
Вывод нечетных чисел: 1 3 5 7
Вывод чисел больше 5: 6 7
Ao mexer nas expressões Lambda, você pode exibir o quadrado de cada elemento da lista. Observe que estamos usando o método stream()para converter uma lista regular em um fluxo. Java 8 fornece uma classe incrível Stream( java.util.stream.Stream). Ele contém vários métodos úteis com os quais você pode usar expressões lambda. Passamos uma expressão lambda x -> x*xpara o método map(), que a aplica a todos os elementos do fluxo. Depois disso usamos forEachpara imprimir todos os elementos da lista.
// Old way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
for(Integer n : list) {
    int x = n * n;
    System.out.println(x);
}
// New way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
list.stream().map((x) -> x*x).forEach(System.out::println);
Dada uma lista, você precisa imprimir a soma dos quadrados de todos os elementos da lista. As expressões lambda permitem que você consiga isso escrevendo apenas uma linha de código. Este exemplo usa o método de convolução (redução) reduce(). Usamos um método map()para elevar ao quadrado cada elemento e, em seguida, usamos um método reduce()para recolher todos os elementos em um único número.
// Old way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = 0;
for(Integer n : list) {
    int x = n * n;
    sum = sum + x;
}
System.out.println(sum);
// New way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get();
System.out.println(sum);

A diferença entre expressões Lambda e classes anônimas

A principal diferença é o uso da palavra-chave this. Para classes anônimas, a palavra-chave ' ' thisdenota um objeto da classe anônima, enquanto em uma expressão lambda, ' this' denota um objeto da classe na qual a expressão lambda é usada. Outra diferença é a forma como são compilados. Java compila expressões lambda e as converte em privatemétodos de classe. Isso usa a instrução invokedynamic , introduzida no Java 7 para ligação de método dinâmico. Tal Weiss descreveu em seu blog como Java compila expressões lambda em bytecode

Conclusão

Mark Reinhold (arquiteto-chefe da Oracle) considerou as expressões Lambda a mudança mais significativa no modelo de programação que já ocorreu - ainda mais significativa do que os genéricos. Ele deve estar certo, porque... eles fornecem aos programadores Java os recursos funcionais da linguagem de programação que todos esperavam. Junto com inovações como métodos de extensão virtual, as expressões Lambda permitem escrever código de altíssima qualidade. Espero que este artigo tenha lhe dado uma visão geral do Java 8. Boa sorte :)
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION