Java 8: Entenda facilmente funções lambda, a principal novidade!

rinaldodev

Rinaldo

Posted on March 24, 2019

Java 8: Entenda facilmente funções lambda, a principal novidade!

A criação de Funções Lambda foi a principal novidade do Java 8, lançado em 2014! Hoje é praticamente obrigatório conhecer como elas funcionam e saber utilizá-las no seu código.

Prefere esse conteúdo em vídeo? Assista aqui!

Java 8 e programação funcional

Há algum tempo o JavaScript veio se estabelecendo como a linguagem padrão de desenvolvimento front-end. Ao mesmo tempo ocorre também o aparecimento e a popularização de linguagens como Scala, Kotlin e Python. Junto desses movimentos, a programação funcional começou a se tornar cada vez mais popular.

Com a intenção de trazer essa possibilidade também para o Java, foi criada a nova sintaxe de funções lambda. Se você nunca viu uma, aqui está:

() -> System.out.println("Hello World")
Enter fullscreen mode Exit fullscreen mode

Se você nunca viu um código assim, não se assuste, você vai entender facilmente o que significa.


Java sem funções lambda

Se você programa Java há algum tempo, provavelmente já teve que escrever algum código parecido com esse:

public static void main(String[] args) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello World");
        }
    }).run();
}
Enter fullscreen mode Exit fullscreen mode

Apesar de esse código funcionar perfeitamente, ele tem um grande problema: é gigante. Pense bem, foram necessárias 6 linhas de código para criar uma Thread e imprimir “Hello World”, algo que deveria ser completamente trivial. Agora, veja esse mesmo código utilizando uma função lambda:

public static void main(String[] args) {
    new Thread(() -> System.out.println("Hello World")).run();
}
Enter fullscreen mode Exit fullscreen mode

O código acima é interpretado pelo compilador exatamente igual ao anterior, tendo uma única linha de código e sendo muito mais conciso. E o mais interessante aqui é que ainda estamos utilizando o mesmo construtor para criar uma Thread. A própria IDE confirma isso:

Java 8: Screenshot do Eclipse mostrando o construtor da classe Thread recebendo uma instância da interface Runnable.
Construtor recebendo uma instância de Runnable

O compilador sabe que essa função lambda é uma instância de Runnable, mesmo que nós não deixemos isso explícito. Ok, mas como ele sabe disso?


Java 8 e o conceito SAM

Esse comportamento do compilador fica simples quando entendemos o conceito de Single Abstract Method, ou SAM. Basicamente, qualquer interface que tenha um único método está seguindo esse conceito. Logo, o compilador entende que sua função lambda é, na verdade, a implementação desse único método. Vamos ver de perto.

A interface Runnable, por exemplo, possui apenas o método run. Aqui está ela, copiada direto da JDK:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
Enter fullscreen mode Exit fullscreen mode

Sendo assim, ao utilizar uma função lambda como fizemos acima, o compilador entende que ela só pode ser a implementação do método run.

Quanto à anotação @FunctionalInterface presente nessa classe, ela é apenas informativa. Ela instrui ao compilador que gere um erro caso essa classe não preencha todos os requisitos para ser uma interface funcional, ou seja, uma que pode ser criada a partir de uma função lambda. Por exemplo, se essa interface tivesse dois métodos, ocorreria um erro de compilação. Apesar disso, essa anotação não é obrigatória. Você pode utilizar funções lambda com qualquer interface que atenda os pré-requisitos para ser considerada funcional.

Ok, então funções lambda servem apenas para eu diminuir a quantidade de linhas de código em casos como esse? Não. A verdade é que as funções lambda são muito mais úteis do que parecem. Os exemplos que dei acima são apenas para entender seu funcionamento, mas o que elas permitem fazer em Java é muito mais interessante.


Programação funcional

Com funções lambda é possível utilizar métodos muito conhecidos para quem utiliza JavaScript, como filter, map e forEach.

Imagine, por exemplo, que você tem uma lista de números. Você deseja imprimir o valor dos 7 primeiros, multiplicado por 2, mas apenas se o número for par. Vejamos uma implementação possível com Java tradicional:

public static void main(String[] args) {
    List<Integer> lista = Arrays.asList(1,5,8,7,4,6,3,2,1,8,5,7,4);
    for (int i = 0; i < 7; i++) {
        Integer numero = lista.get(i);
        if (numero % 2 == 0) {
            System.out.println(numero * 2);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Essa é uma implementação comum, funciona perfeitamente e, se você programa em Java há algum tempo, provavelmente está acostumado a vê-la. Porém, vamos ver como seria a mesma implementação com o uso de Streams do Java 8:

public static void main(String[] args) {
    List<Integer> lista = Arrays.asList(1,5,8,7,4,6,3,2,1,8,5,7,4);
    lista.stream()
        .limit(7)
        .filter(e -> e % 2 == 0)
        .map(e -> e * 2)
        .forEach(System.out::println);
}
Enter fullscreen mode Exit fullscreen mode

Para quem não está acostumado, essa implementação pode parecer estranha a primeira vez. Porém, ela é muito mais concisa e delimitada. É possível saber exatamente todas as operações que estão sendo feitas nessa lista. Claramente existe um limite (limit), um filtro (filter), uma transformação (map) e uma ação para cada item (forEach).

Essa é estrutura da função lambda passada para o método filter:

Java 8: estrutura da função lambda. Primeiro temos os parâmetros da função, depois o arrow token
Estrutura da função lambda passada no método filter
", depois do corpo da função."/>

A partir do momento que você aprende funções lambda e a nova API de Streams, você escreve um código muito mais fácil de entender, logo, mais fácil de dar manutenção. Além disso, muito menos propício a bugs.


A real vantagem do Java 8

De fato, a principal vantagem das funções lambdas no Java 8 foi permitir o uso de Streams. Sem elas seria praticamente impossível utilizar Streams com facilidade. O código seria tão grande e complicado, que simplesmente não seria útil. Se você está curioso para saber como ficaria a implementação acima sem o uso de funções lambda, aqui está. Por favor, nunca faça isso.

public static void main(String[] args) {
    List<Integer> lista = Arrays.asList(1, 5, 8, 7, 4, 6, 3, 2, 1, 8, 5, 7, 4);
    Stream<Integer> limit = lista.stream().limit(7);
    // nunca escreva um código assim!
    Stream<Integer> filter = limit.filter(new Predicate<Integer>() {
        @Override
        public boolean test(Integer e) {
            return e % 2 == 0;
        }
    });
    Stream<Integer> map = filter.map(new Function<Integer, Integer>() {
        @Override
        public Integer apply(Integer e) {
            return e * 2;
        }
    });
    map.forEach(new Consumer<Integer>() {
        @Override
        public void accept(Integer t) {
            System.out.println(t);
        }
    });
}
Enter fullscreen mode Exit fullscreen mode

Um caminho para o futuro do Java

Apesar de funções lambda terem sido extremamente úteis para o lançamento da API de Streams, isso foi apenas o começo. Várias APIs estão se tornando possíveis com o uso de funções lambda. O Java 9, por exemplo, trouxe Reactive Streams, o padrão implementado pela famosa biblioteca RxJava.

Em breve provavelmente estaremos implementando muitas coisas com programação funcional e funções lambdas no Java, como requisições HTTP, operações com arquivos e comunicação com banco de dados.

Quer saber um pouco mais da utilidade de funções lambda? Veja esse outro artigo sobre Streams e como utilizar funções lambda com coleções.

Parabéns! Agora você conhece expressões lambda do Java 8, e pode utilizar esse conhecimento para entregar seu projeto mais rápido e crescer na sua carreira.

Quer acessar esse conteúdo em vídeo também? Assista aqui!

Quer receber minhas melhores dicas para escrever código de alta qualidade e entregar projetos no prazo? Então acesse aqui.

Quer aprender a melhorar seu código diariamente? Então me segue no twitter: https://twitter.com/rinaldodev


Você já viu algum código com funções lambda recentemente? Ou conhece outras situações em que poderíamos utilizar? Deixe um comentário! Compartilhe também!

Gostou do que aprendeu? Compartilhe com outros Devs!

O post Java 8: Entenda facilmente funções lambda, a principal novidade! apareceu primeiro em rinaldo.dev.

💖 💪 🙅 🚩
rinaldodev
Rinaldo

Posted on March 24, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related