Expressão Lambda em Java na Prática

Compartilhar:

As expressões lambda, introduzidas no Java 8, representam um dos avanços mais significativos na linguagem desde sua criação.

Elas permitem que você trate funcionalidades como métodos de primeira classe ou expressões anônimas, facilitando a implementação de interfaces funcionais (interfaces com um único método abstrato).

O que é uma Expressão Lambda?

Uma expressão lambda é uma forma concisa de escrever implementações de interfaces funcionais. Elas permitem que você escreva métodos anônimos de maneira clara e concisa.

As expressões lambda ajudam a reduzir o código boilerplate, tornando o código mais legível e fácil de manter.

lambda em java

Sintaxe Básica

A sintaxe básica de uma expressão lambda é composta de três partes:

  1. Uma lista de parâmetros entre parênteses ().
  2. A seta de lambda ->.
  3. O corpo da expressão, que pode ser uma única expressão ou um bloco de código.

Sintaxe:

(parameters) -> expression
ou
(parameters) -> { statements; }

Exemplos Simples de Lambda em Java

Abaixo, separamos 4 exemplos simples para ilustrar a sintaxe das expressões lambda em Java:

1. Lambda sem parâmetros:

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

2. Lambda com um parâmetro:

x -> x * x

3. Lambda com múltiplos parâmetros:

(x, y) -> x + y

4. Lambda com um bloco de código:

(x, y) -> {
    int sum = x + y;
    return sum;
}

Exemplos de Uso do Lambda em Java

As expressões lambda são usadas em muitos contextos no Java, sendo os mais comuns:

Com a interface Runnable:

Runnable r = () -> System.out.println("Running in a separate thread");
new Thread(r).start();

Com a interface Comparator:

Comparator<String> comparator = (s1, s2) -> s1.compareToIgnoreCase(s2);
List<String> list = Arrays.asList("a", "B", "c", "D");
Collections.sort(list, comparator);

Com a API de Streams:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squares = numbers.stream()
                               .map(x -> x * x)
                               .collect(Collectors.toList());

Agora vamos fazer um pouco diferente.

Uma classe vai demonstrar o uso básico de expressões lambda, e a outra vai focar na utilização da API de Streams.

Classe 1: Uso Básico de Expressões Lambda

package br.com.virandoprogramador.lambda

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class LambdaBasics {

    public static void main(String[] args) {
        // Exemplo 1: Uso com Runnable
        Runnable runnable = () -> System.out.println("Executando em uma thread separada");
        new Thread(runnable).start();

        // Exemplo 2: Uso com Comparator
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Dave");
        System.out.println("Lista original: " + names);

        // Ordenar a lista sem distinção entre maiúsculas e minúsculas
        Comparator<String> caseInsensitiveComparator = (s1, s2) -> s1.compareToIgnoreCase(s2);
        Collections.sort(names, caseInsensitiveComparator);
        System.out.println("Lista ordenada (case insensitive): " + names);

        // Exemplo 3: Uso com uma interface funcional customizada
        MyFunctionalInterface greeting = (name) -> System.out.println("Olá, " + name);
        greeting.sayHello("Mundo");

        // Exemplo 4: Passando comportamento como argumento
        performOperation(5, x -> x * x); // Imprime: O resultado da operação é: 25
        performOperation(7, x -> x + 10); // Imprime: O resultado da operação é: 17
    }

    @FunctionalInterface
    interface MyFunctionalInterface {
        void sayHello(String name);
    }

    public static void performOperation(int value, MyOperation operation) {
        int result = operation.operate(value);
        System.out.println("O resultado da operação é: " + result);
    }

    @FunctionalInterface
    interface MyOperation {
        int operate(int x);
    }
}

Classe 2: Uso de Expressões Lambda com API de Streams

package br.com.virandoprogramador.lambda

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

public class LambdaStreams {

    public static void main(String[] args) {
        List<String> strings = Arrays.asList("apple", "banana", "cherry", "date", "elderberry", "fig", "grape");

        // Exemplo 1: Filtrar strings que começam com 'a' e coletar em uma lista
        List<String> filteredStrings = strings.stream()
                                              .filter(s -> s.startsWith("a"))
                                              .collect(Collectors.toList());
        System.out.println("Strings que começam com 'a': " + filteredStrings);

        // Exemplo 2: Converter strings para maiúsculas e coletar em um set
        Set<String> upperCaseStrings = strings.stream()
                                              .map(String::toUpperCase)
                                              .collect(Collectors.toSet());
        System.out.println("Strings em maiúsculas: " + upperCaseStrings);

        // Exemplo 3: Encontrar a string mais longa
        Optional<String> longestString = strings.stream()
                                                .reduce((s1, s2) -> s1.length() > s2.length() ? s1 : s2);
        longestString.ifPresent(s -> System.out.println("A string mais longa é: " + s));

        // Exemplo 4: Agrupar strings pelo seu comprimento
        Map<Integer, List<String>> stringsByLength = strings.stream()
                                                            .collect(Collectors.groupingBy(String::length));
        System.out.println("Strings agrupadas por comprimento: " + stringsByLength);

        // Exemplo 5: Concatenar todas as strings com um delimitador
        String concatenatedStrings = strings.stream()
                                            .collect(Collectors.joining(", "));
        System.out.println("Strings concatenadas: " + concatenatedStrings);

        // Exemplo 6: Contar o número de strings que contêm a letra 'e'
        long count = strings.stream()
                            .filter(s -> s.contains("e"))
                            .count();
        System.out.println("Número de strings que contêm 'e': " + count);
    }
}

Explicação dos Exemplos

Classe 1: LambdaBasics

  1. Runnable: Criação e execução de uma thread com uma expressão lambda.
  2. Comparator: Uso de uma expressão lambda para comparar strings sem distinção entre maiúsculas e minúsculas.
  3. Interface Funcional Customizada: Definição e uso de uma interface funcional para imprimir saudações.
  4. Passagem de Comportamento: Uso de expressões lambda para passar comportamentos como argumentos para um método.

Classe 2: LambdaStreams

  1. Filtragem: Filtragem de strings que começam com uma letra específica.
  2. Mapeamento: Conversão de strings para maiúsculas e coleta em um conjunto.
  3. Redução: Encontrar a string mais longa usando a operação de redução.
  4. Agrupamento: Agrupar strings pelo comprimento.
  5. Junção: Concatenar todas as strings com um delimitador.
  6. Contagem: Contar o número de strings que contêm uma letra específica.

Esses exemplos ilustram a flexibilidade e o poder das expressões lambda em Java, desde o uso básico até operações mais complexas com a API de Streams.

Motivos para Utilizar Expressões Lambda em Java

As expressões lambda representam um dos avanços mais importantes na linguagem Java, especialmente com a introdução do Java 8. Aqui estão os principais motivos pelos quais você deve considerar o uso de expressões lambda em seus projetos Java:

1. Código Mais Conciso e Legível

Um dos principais benefícios das expressões lambda é a redução do código boilerplate. Tradicionalmente, a implementação de interfaces anônimas em Java exige uma quantidade significativa de código repetitivo.

Com as expressões lambda, você pode escrever o mesmo comportamento em uma linha de código, tornando o código mais legível e fácil de manter.

2. Facilitação da Programação Funcional

As expressões lambda permitem que você adote um estilo de programação mais funcional. Em vez de se concentrar em como realizar uma tarefa (programação imperativa), você pode se concentrar no que precisa ser feito (programação declarativa). Isso resulta em um código que é mais fácil de entender e raciocinar.

3. Melhor Integração com a API de Streams

A API de Streams, também introduzida no Java 8, permite operações de manipulação de dados de maneira declarativa e fluente.

As expressões lambda são uma parte essencial dessa API, facilitando operações como map, filter, e reduce. Isso permite que você realize transformações e processamentos complexos em coleções de dados de forma mais simples e eficiente.

4. Aumento da Produtividade do Desenvolvedor

Ao simplificar a sintaxe e reduzir a quantidade de código necessário para realizar tarefas comuns, as expressões lambda aumentam a produtividade dos desenvolvedores.

Menos código significa menos espaço para erros e mais tempo focado na lógica de negócios em vez de detalhes de implementação.

5. Aprimoramento da Concorrência e Paralelismo

A programação funcional, facilitada pelas expressões lambda, é naturalmente mais adequada para execução paralela.

Operações de streams podem ser paralelizadas facilmente, aproveitando melhor os recursos de hardware modernos e melhorando o desempenho das aplicações que processam grandes volumes de dados.

6. Facilidade na Implementação de Callbacks e Eventos

As expressões lambda tornam a implementação de callbacks e eventos mais simples e intuitiva.

Em vez de criar classes anônimas ou subclasses para tratar eventos, você pode passar diretamente a lógica a ser executada em forma de expressão lambda, tornando o código de eventos mais limpo e direto.

7. Flexibilidade e Reutilização

As expressões lambda permitem passar comportamentos como argumentos para métodos ou armazená-los em variáveis. Isso proporciona uma maior flexibilidade na composição de funções e na reutilização de código.

Você pode criar pequenas funções reutilizáveis e combiná-las de maneiras novas e interessantes para resolver problemas complexos.

8. Compatibilidade com Código Legado

Apesar das melhorias que trazem, as expressões lambda são compatíveis com o código legado.

Elas podem ser introduzidas gradualmente em projetos existentes, permitindo que você modernize partes do código sem precisar reescrever tudo. Isso facilita a adoção de novas práticas sem um alto custo inicial.

9. Suporte a Interfaces Funcionais Personalizadas

Além de utilizar interfaces funcionais padrão como Runnable e Comparator, você pode definir suas próprias interfaces funcionais para atender a necessidades específicas.

As expressões lambda podem ser usadas para implementar essas interfaces de maneira clara e concisa, aumentando a expressividade do seu código.

Finalizando sobre Lambda

As expressões lambda são uma poderosa adição à linguagem Java, oferecendo uma maneira mais concisa e legível de escrever código.

Elas facilitam a programação funcional, melhoram a integração com a API de Streams, aumentam a produtividade do desenvolvedor e aprimoram a concorrência e o paralelismo.

Além disso, permitem a implementação de callbacks e eventos de forma mais simples e oferecem maior flexibilidade e reutilização de código. Tudo isso, juntamente com a compatibilidade com código legado e o suporte a interfaces funcionais personalizadas, faz das expressões lambda uma ferramenta essencial para qualquer desenvolvedor Java moderno.

5/5 - (1 vote)
Compartilhar:

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *