English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Tutoriais Básicos de Java

Controle de Fluxo Java

Java Array

Java Orientação a Objetos (I)

Java Orientação a Objetos (II)

Java Orientação a Objetos (III)

Tratamento de Exceções Java

Java Lista (List)

Java Fila (Queue)

Java Mapa (Map)

Java Conjunto (Set)

Java Entrada e Saída (I)/O)

Java Reader/Writer

Outros tópicos Java

Expressões Lambda Java

Neste artigo, vamos entender a expressão lambda Java por meio de exemplos, bem como o uso de interfaces funcionais, interfaces genéricas de funções e API de fluxo.

A expressão lambda é no Java 8Pela primeira vez introduzido. Seu principal objetivo é melhorar a capacidade de expressão do idioma.

Mas, antes de aprender lambda, precisamos primeiro entender as interfaces funcionais.

O que é uma interface funcional?

Se o interface Java contiver apenas um método abstrato, ele é chamado de interface funcional. Apenas esse método especifica o uso esperado da interface.

例如,包java.lang中的Runnable接口;是一个功能接口,因为它只包含一个方法,即run()。

Exemplo1:在java中定义功能接口

import java.lang.FunctionalInterface;
@FunctionalInterface
public interface MyInterface{
    //单一抽象方法
    double getValue();
}

在上面的示例中,接口MyInterface只有一个抽象方法getValue()。因此,它是一个功能接口。

在这里,我们使用了注解@FunctionalInterface。该注解会强制Java编译器指示该接口是功能接口。因此,不允许有多个抽象方法。但是,它不是强制性的。

在Java 7中,功能接口被视为单一抽象方法(SAM)类型。在Java 7中,SAM类型通常是通过匿名类实现的。

Exemplo2:使用Java中的匿名类实现SAM

public class FunctionInterfaceTest {
    public static void main(String[] args) {
        //匿名类
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我刚刚实现了Runnable功能接口。");
            }
        }).start();
    }
}

Saída

我刚刚实现了Runnable功能接口。

在这里,我们可以将匿名类传递给方法。这有助于用Java 7编写更少的程序。但是,语法仍然很困难,需要大量的额外代码行。

Java 8进一步扩展了SAM的功能。由于我们知道功能接口只有一个方法,因此在将其作为参数传递时,无需定义该方法的名称。Lambda表达式使我们能够做到这一点。

Lambda表达式简介

Lambda表达式本质上是一个匿名或未命名的函数。Lambda表达式不能单独执行。相反,它用于实现功能接口定义的方法。

如何在Java中定义Lambda表达式?

这是我们如何在Java中定义lambda表达式。

(lista de parâmetros) -> corpo do lambda

使用的新运算符(->) chamado operador de seta ou operador lambda. Vamos explorar alguns exemplos,

假设我们有一个这样的方法:

double getPiValue() {
    return 3.1415;
}

Podemos escrever este método usando a expressão lambda, conforme mostrado:

(); -> 3.1415

Aqui, o método não tem nenhum parâmetro. Portanto, o lado esquerdo do operador inclui um parâmetro vazio. O lado direito é o corpo de lambda, que especifica a operação da expressão lambda. Neste caso, ele retornará o valor3.1415。

Tipos de corpo de lambda

No Java, os corpos de lambda têm dois tipos.

1. Corpo de expressão único

(); -> System.out.println("Lambdas são ótimas");

Este tipo de corpo de lambda é chamado de corpo de expressão.

2. Corpo principal composto por código.

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

Este tipo de corpo de lambda é chamado de corpo de bloco. O corpo de bloco permite que o corpo de lambda contenha várias instruções. Essas instruções estão entre parênteses e você deve adicionar um ponto e vírgula após os parênteses.

Atenção: Para o corpo do bloco, você sempre deve ter uma instrução return. No entanto, um corpo de expressão única não precisa de instrução return.

Exemplo3: Expressão lambda

Vamos escrever um programa Java que usa a expressão lambda para retornar o valor de Pi.

Como mencionado anteriormente, a expressão lambda não é executada isoladamente. Em vez disso, ela forma a implementação do método abstrato definido pela interface funcional.

Portanto, precisamos definir primeiro uma interface funcional.

import java.lang.FunctionalInterface;
//Isso é uma interface funcional
@FunctionalInterface
interface MyInterface{
    // 抽象方法
    double getPiValue();
}
public class Main {
    public static void main(String[] args) {
    //声明对MyInterface的引用
    MyInterface ref;
    
    // expressão lambda
    ref = () -> 3.1415;
    
    System.out.println("Pi = "); + ref.getPiValue());
    } 
}

Saída

Pi = 3.1415

No exemplo acima,

  • Criamos uma interface funcional chamada MyInterface. Ela contém um método abstrato chamado getPiValue().

  • Dentro da classe Main, declaramos uma referência para MyInterface. Observe que podemos declarar uma referência para a interface, mas não instanciar a interface. Isso é porque,
     

    //Isso lançará um erro
    MyInterface ref = new myInterface();
    // Isso é válido
    MyInterface ref;
  • Em seguida, atribuímos uma expressão lambda à referência.

    ref = () -> 3.1415;
  • Por fim, usamos o método getPiValue() da interface reference.

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

Expressões lambda com parâmetros

Até agora, já criamos expressões lambda sem qualquer parâmetro. Mas, como métodos, as expressões lambda também podem ter parâmetros. Por exemplo,

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

在此,括号内的变量n是传递给lambda表达式的参数。Lambda主体接受参数并检查其是偶数还是奇数。

Exemplo4:将lambda表达式与参数一起使用

@FunctionalInterface
interface MyInterface {
    //抽象方法
    String reverse(String n);
}
public class Main {
    public static void main(String[] args) {
                //声明对MyInterface的引用
                //将lambda表达式分配给引用
        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"));
    }
}

Saída

Lambda reversed = adbmaL

泛型功能接口

到目前为止,我们已经使用了仅接受一种类型的值的功能接口。例如,

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

上面的功能接口仅接受String并返回String。但是,我们可以使功能接口通用,以便接受任何数据类型。如果不熟悉泛型,请访问Java泛型

Exemplo5:泛型功能接口和Lambda表达式

// GenericInterface.java
@FunctionalInterface
interface GenericInterface<T> {
    // 泛型方法
    T func(T t);
}
// GenericLambda.java
public class Main {
    public static void main(String[] args) {
                //声明对GenericInterface的引用
                // GenericInterface对String数据进行操作
                //为其分配一个lambda表达式
        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"));
                //声明对GenericInterface的另一个引用
                // GenericInterface对整数数据进行操作
                //为其分配一个lambda表达式
        GenericInterface<Integer> factorial = (n) -> {
            int result = 1;
            for (int i = 1; i <= n; i++)
            result = i * result;
            return result;
        };
        System.out.println("5fatorial = " + factorial.func(5));
    }
}

Saída

Lambda reversed = adbmaL
5fatorial = 120

No exemplo acima, criamos uma interface funcional genérica chamada GenericInterface. Ela contém um método genérico chamado func().

Dentro da classe:

  • GenericInterface<String> reverse - Criar uma referência para essa interface. Agora, a interface pode manipular dados do tipo String.

  • GenericInterface<Integer> factorial -Criar uma referência para essa interface. Neste caso, a interface opera com dados do tipo Integer.

Expressões lambda e API Stream

novosjava.util.streampacote foi adicionado ao JDK8que permite que desenvolvedores Java executem buscas, filtros, mapeamentos, reduções e operações em listas e outros conjuntos.

Por exemplo, temos um fluxo de dados (em nosso exemplo, uma lista de strings), onde cada string é o nome do país e do/Combinação de regiões. Agora, podemos processar esse fluxo de dados e recuperar apenas localizações do Nepal.

Para isso, podemos combinar o API Stream e a expressão lambda para executar operações em lote no fluxo.

Exemplo6:Demonstrar o uso de lambda com o API Stream

import java.util.ArrayList;
import java.util.List;
public class StreamMain {
    //Usar ArrayList para criar um objeto de lista
    static List<String> places = new ArrayList<>();
    //Preparar nossos dados
    public static List getPlaces(){
        //Adicionar localidades e países à lista
        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("Lugares de Nepal:");
        
        myPlaces.stream()
                .filter((p) -> p.startsWith("Nepal"))
                .map((p) -> p.toUpperCase())
                .sorted()
                .forEach((p) -> System.out.println(p));
    }
}

Saída

Lugares de Nepal:
NEPAL, KATHMANDU
NEPAL, POKHARA

Neste exemplo, note as seguintes declarações:

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() e forEach() da API Stream. Esses métodos podem usar expressões lambda como entrada.

Podemos definir nossas próprias expressões com base na sintaxe estudada acima. Como mostrado no exemplo, isso nos permite reduzir significativamente o número de linhas de código.