Expressões lambda em Java

Olá, Habr! Apresento a sua atenção a tradução do artigo "Java Lambda Expressions" do autor de www.programiz.com .



Introdução



Neste artigo, usando exemplos, exploraremos as expressões lambda em Java, seu uso com interfaces funcionais, interfaces funcionais parametrizadas e APIs de fluxo.



Expressões lambda foram adicionadas em Java 8. Seu objetivo principal é melhorar a legibilidade e reduzir a quantidade de código.



Mas antes de passar para lambdas, precisamos entender as interfaces funcionais.



O que é uma interface funcional?



Se uma interface em Java contém um e apenas um método abstrato, ela é chamada de funcional. Este único método determina o propósito da interface.



Por exemplo, a interface Runnable do pacote java.lang é funcional porque contém apenas um método run ().



Exemplo 1: declaração de uma interface funcional em java



import java.lang.FunctionalInterface;
@FunctionalInterface
public interface MyInterface{
    //   
    double getValue();
}


No exemplo acima, a interface MyInterface tem apenas um método abstrato, getValue (). Portanto, esta interface é funcional.



Aqui nós usamos anotaçãoFunctionalInterfaceo que ajuda o compilador a entender que a interface é funcional. Portanto, não permite mais de um método abstrato. No entanto, podemos omiti-lo.



No Java 7, as interfaces funcionais eram tratadas como Single Abstract Methods (SAM). SAMs geralmente eram implementados usando classes anônimas.



Exemplo 2: implementação de SAM com classe anônima em java



public class FunctionInterfaceTest {
    public static void main(String[] args) {

        //  
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("      Runnable.")
            }
        }).start();
    }
}


Resultado da execução:



      Runnable.


Neste exemplo, aceitamos uma classe anônima para chamar o método. Isso ajudou a escrever programas com menos linhas de código em Java 7. No entanto, a sintaxe permaneceu bastante complexa e complicada.



O Java 8 expandiu os recursos do SAM, dando um passo adiante. Como sabemos, a interface funcional contém apenas um método, portanto, não precisamos especificar o nome do método ao passá-lo como um argumento. Isso é exatamente o que as expressões lambda nos permitem fazer.



Introdução às expressões lambda



As expressões lambda são essencialmente uma classe ou método anônimo. A expressão lambda não é executada por conta própria. Em vez disso, é usado para implementar o método definido na interface funcional.



Como faço para escrever uma expressão lambda em Java?



Em Java, as expressões lambda têm a seguinte sintaxe:



(parameter list) -> lambda body


Aqui, usamos um novo operador (->) - o operador lambda. Talvez a sintaxe pareça um pouco complicada. Vejamos alguns exemplos.



Digamos que temos um método como este:



double getPiValue() {
    return 3.1415;
}


Podemos escrever usando um lambda como:



() -> 3.1415


Este método não tem parâmetros. Portanto, o lado esquerdo da expressão contém parênteses vazios. O lado direito é o corpo da expressão lambda, que define sua ação. Em nosso caso, o valor de retorno é 3,1415.



Tipos de expressões lambda



Em Java, o corpo de um lambda pode ser de dois tipos.



1. One-line



() -> System.out.println("Lambdas are great");


2. Bloco (multilinhas)



() -> {
    double pi = 3.1415;
    return pi;
};


Este tipo permite que uma expressão lambda tenha várias operações internamente. Essas operações devem ser colocadas entre chaves, seguidas por um ponto e vírgula.



Nota: As expressões lambda de várias linhas devem sempre ter uma instrução de retorno, ao contrário das expressões de uma linha.



Exemplo 3: Expressão Lambda



Vamos escrever um programa Java que retorna Pi usando uma expressão lambda.



Conforme declarado anteriormente, uma expressão lambda não é executada automaticamente. Em vez disso, ele forma uma implementação de um método abstrato declarado em uma interface funcional.



E então, primeiro, precisamos descrever a interface funcional.



import java.lang.FunctionalInterface;

//  
@FunctionalInterface
interface MyInterface{

    //  
    double getPiValue();
}
public class Main {

    public static void main( String[] args ) {

    //    MyInterface
    MyInterface ref;
    
    // -
    ref = () -> 3.1415;
    
    System.out.println("Value of Pi = " + ref.getPiValue());
    } 
}


Resultado da execução:



Value of Pi = 3.1415


No exemplo acima:



  • Criamos uma interface funcional, MyInterface, que contém um método abstrato, getPiValue ().
  • Dentro da classe Main, declaramos uma referência a MyInterface. Observe que podemos declarar uma referência de interface, mas não podemos criar um objeto dela.



    //   
    	MyInterface ref = new myInterface();
    	//  
    	MyInterface ref;
    


  • Em seguida, atribuímos uma expressão lambda ao link



    ref = () -> 3.1415;
  • Finalmente, chamamos o método getPiValue () usando a referência da interface.



    System.out.println("Value of Pi = " + ref.getPiValue());


Expressões lambda com parâmetros



Até este ponto, criamos expressões lambda sem nenhum parâmetro. No entanto, assim como os métodos, lambdas podem ter parâmetros.



(n) -> (n % 2) == 0


Neste exemplo, a variável n entre parênteses é o parâmetro passado para a expressão lambda. O corpo lambda pega um parâmetro e verifica a paridade.



Exemplo 4: usando uma expressão lambda com parâmetros



@FunctionalInterface
interface MyInterface {

    //  
    String reverse(String n);
}

public class Main {

    public static void main( String[] args ) {

        //    MyInterface
        //  - 
        MyInterface ref = (str) -> {

            String result = "";
            for (int i = str.length()-1; i >= 0 ; i--)
            result += str.charAt(i);
            return result;
        };
        //    
        System.out.println("Lambda reversed = " + ref.reverse("Lambda"));
    }

}


Resultado da execução:



Lambda reversed = adbmaL


Interface funcional parametrizada



Até este ponto, usamos interfaces funcionais que aceitam apenas um tipo de valor. Por exemplo:



@FunctionalInterface
interface MyInterface {
    String reverseString(String n);
}


A interface funcional acima aceita apenas String e retorna String. No entanto, podemos tornar nossa interface genérica para usar com qualquer tipo de dados.



Exemplo 5: interface parametrizada e expressões lambda



//  
@FunctionalInterface
interface GenericInterface<T> {

    //  
    T func(T t);
}

public class Main {

    public static void main( String[] args ) {

        //     
        //   String
        //    
        GenericInterface<String> reverse = (str) -> {

            String result = "";
            for (int i = str.length()-1; i >= 0 ; i--)
            result += str.charAt(i);
            return result;
        };

        System.out.println("Lambda reversed = " + reverse.func("Lambda"));

        //     
        //   Integer
        //    
        GenericInterface<Integer> factorial = (n) -> {

            int result = 1;
            for (int i = 1; i <= n; i++)
            result = i * result;
            return result;
        };

        System.out.println("factorial of 5 = " + factorial.func(5));
    }
}


Resultado da execução:



Lambda reversed = adbmaL
factorial of 5 = 120


Neste exemplo, criamos uma interface funcional parametrizada GenericInterface que contém um método func () parametrizado.



Então, dentro da classe principal:



  • GenericInterface <String> reverse - cria um link para uma interface que funciona com String.
  • GenericInterface <Integer> factorial - Cria um link para uma interface que funciona com Integer.


Expressões Lambda e API Stream



O JDK8 adiciona um novo pacote, java.util.stream, que permite aos desenvolvedores Java realizar operações como pesquisa, filtragem, correspondência, concatenação ou manipulação de coleções como Listas.



Por exemplo, temos um fluxo de dados (no nosso caso, uma lista de strings), onde cada string contém o nome de um país e sua cidade. Agora podemos processar esse fluxo de dados e selecionar apenas as cidades do Nepal.



Para fazer isso, podemos usar uma combinação de API Stream e expressões lambda.



Exemplo 6: Usando Lambdas na API Stream



import java.util.ArrayList;
import java.util.List;

public class StreamMain {

    //  
    static List<String> places = new ArrayList<>();

    //  
    public static List getPlaces(){

        //    
        places.add("Nepal, Kathmandu");
        places.add("Nepal, Pokhara");
        places.add("India, Delhi");
        places.add("USA, New York");
        places.add("Africa, Nigeria");

        return places;
    }

    public static void main( String[] args ) {

        List<String> myPlaces = getPlaces();
        System.out.println("Places from Nepal:");
        
        //  
        myPlaces.stream()
                .filter((p) -> p.startsWith("Nepal"))
                .map((p) -> p.toUpperCase())
                .sorted()
                .forEach((p) -> System.out.println(p));
    }

}


Resultado da execução:



Places from Nepal:
NEPAL, KATHMANDU
NEPAL, POKHARA


No exemplo acima, observe esta expressão:



myPlaces.stream()
        .filter((p) -> p.startsWith("Nepal"))
        .map((p) -> p.toUpperCase())
        .sorted()
        .forEach((p) -> System.out.println(p));


Aqui, usamos métodos como filter (), map (), forEach () da API Stream, que pode receber lambdas como parâmetro.



Além disso, podemos descrever nossas próprias expressões com base na sintaxe descrita acima. Isso nos permitirá reduzir o número de linhas de código.



All Articles