Executando verificação de segurança...
1

Funcionalidades do JDK 8 (Java 8) - Parte II - Funções Lambda

Defaut Methods

Repare que utilizamos o método forEach() acima, invocando ele a partir de uma List, porém se você for olhar a interface java.util.List verá que lá não tem nenhum método forEach(), isso acontece por que esse método está declarado na interface java.lang.Iterable.

Isso foi feito assim, pois se não todo mundo que um dia implementou um List teria seus código quebrados, pois seriam obrigados a implementar também esse método. Como List, herda de Collection e essa herda de Iterable então podemos invovar o forEach().

classDiagram
class Iterable {
    +forEach()
}
Iterable <|-- Collection : extends
Collection <|-- List : extends

Além disso bibliotecas que tem implementações de List como é o caso do Hibernate também sofreriam com o breaking change.

Porém se simplesmente tivessem acrescentado o método forEach() na interface Collection, todo mundo que herda deveria implementar, e por que isso não acontece ? Vamos dar uma olhadinha no código do forEach():

public interface Iterable<T> {

    ...

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
}

Opa, opa, opa !!! Como assim, um método de interface com implementação ? Sim !!! É isso que faz com que o breaking change não cause efeitos colaterais, note o modificar default antes da declaração do método.

Esses são os default methods, e foi uma necessidade cria-los para poder evoluir a JDK sem causar muito transtorno a coisas já solidamente implementadas.

Note que o forEach() espera receber como argumento uma implementação de uma interface funcional chamada Consumer<T> que por sua vez possui apenas um método abstrato chamado accept() que recebe o tipo parametrizado T e faz o que você quiser com ele.

Se você realmente prestou atenção, deve ter notado que o tempo todo eu falei que Consumer<T> possui apenas um método abstrato, porém se você leu o código fonte da interface, notou que ela tem outros métodos que são default methods, dito isso, entendemos que uma interface funcional pode ter mais de um método desde que eles sejam dafult methods, como por exemplo o default methodo andThen()

@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

Esse método pode ser utilizado para compor instâncias da interface Consumer como no exemplo a seguir:

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

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

        Piloto senna = new Piloto("Ayrton Senna", 1000);
        Piloto prost = new Piloto("Alain Prost", 10);
        Piloto gasly = new Piloto("Pierre Gasly", 908);

        List<Piloto> pilotos = Arrays.asList(senna, prost, gasly);

        Consumer<Piloto> exibirMensagem = piloto -> System.out.println("\nOlá ");

        Consumer<Piloto> exibirNome = piloto -> System.out.println(piloto.getNome());

        pilotos.forEach(exibirMensagem.andThen(exibirNome));
    }
}

Falando nisso, sobre a interface Collection, ela também recebeu default methods dentre eles um bem interessante que é o removeIf(), vamos ver como ele funciona.

Esse método recebe um predicado, que nada mais é do que uma condicional para que se execute algo.

Vamos a um código de exemplo para demonstrar seu uso, primeiro vamos criar uma lista de frutas

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

public class Main {
    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>();

        fruits.add("maçã");
        fruits.add("banana");
        fruits.add("abacaxi");
        fruits.add("pera");
        fruits.add("uva");
    }
}

Agora vamos criar um predicado

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

public class Main {
    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>();

        fruits.add("maçã");
        fruits.add("banana");
        fruits.add("abacaxi");
        fruits.add("pera");
        fruits.add("uva");


        Predicate<String> predicado = new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.contains("r");
            }
        };
    }
}

E por fim aplicar esse predicado à lista passando ele como argumento do método removeIf()

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


        Predicate<String> predicado = new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.contains("r");
            }
        };

        fruits.removeIf(predicado);
        fruits.forEach(fruit -> System.out.println(fruit));
    }
}

Após a execução do removeIf() todas as frutas que contém a letra "r" serão removidas da lista.

Note que utilizamos ArrayList e não Arrays.asList() isso por que a segunda é uma lista imutável e não permitiria a remoção de um item.

Esse mesmo código poderia ser reescrito utilizando lambdas da seguinte forma:

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

public class Main {
    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>();

        fruits.add("maçã");
        fruits.add("banana");
        fruits.add("abacaxi");
        fruits.add("pera");
        fruits.add("uva");


        Predicate<String> predicado = s -> s.contains("r");

        fruits.removeIf(predicado);
        fruits.forEach(System.out::println);
    }
}

Note que temos uma implementação mais enxuta.

Agora que você entendeu como um predicado funciona, vamos para algo mais utilizado na vida real.

Tenha em mente a nossa classe Piloto escrita acima e seguinte código:

package br.com.jorgerabellodev;

import br.com.jorgerabellodev.domain.model.Piloto;

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

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

        Piloto senna = new Piloto("Ayrton Senna", 100);
        Piloto prost = new Piloto("Alain Prost", 10);
        Piloto gasly = new Piloto("Pierre Gasly", 690);

        List<Piloto> pilotos = new ArrayList<>();

        pilotos.add(senna);
        pilotos.add(prost);
        pilotos.add(gasly);

    }
}

Agora imagine que desejamos excluir da lista todos os usuário com menos de 100 pontos.

Então vamos construir um predicado e aplicar ele utilizando o removeIf()

package br.com.jorgerabellodev;

import br.com.jorgerabellodev.domain.model.Piloto;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

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

        Piloto senna = new Piloto("Ayrton Senna", 100);
        Piloto prost = new Piloto("Alain Prost", 10);
        Piloto gasly = new Piloto("Pierre Gasly", 690);

        List<Piloto> pilotos = new ArrayList<>();

        pilotos.add(senna);
        pilotos.add(prost);
        pilotos.add(gasly);


        Predicate<Piloto> pilotosComMenosDe100Pontos = new Predicate<Piloto>() {
            @Override
            public boolean test(Piloto piloto) {
                return piloto.getPontuacao() < 100;
            }
        };

        pilotos.removeIf(pilotosComMenosDe100Pontos);

        pilotos.forEach(piloto -> System.out.println(piloto));
    }
}

O código cima ainda pode ser resumido utilizando lambdas

package br.com.jorgerabellodev;

import br.com.jorgerabellodev.domain.model.Piloto;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

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

        Piloto senna = new Piloto("Ayrton Senna", 100);
        Piloto prost = new Piloto("Alain Prost", 10);
        Piloto gasly = new Piloto("Pierre Gasly", 690);

        List<Piloto> pilotos = new ArrayList<>();

        pilotos.add(senna);
        pilotos.add(prost);
        pilotos.add(gasly);


        Predicate<Piloto> predicate = piloto -> piloto.getPontuacao() < 100;

        pilotos.removeIf(predicate);

        pilotos.forEach(System.out::println);
    }
}

ATENÇÃO: Os default methods foram criados para que fosse possível que interfaces pudessem evoluir sem quebrar código existente. Eles não foram criados para permitr herança múltipla ou mixins. O que eu quero dizer com isso é que não há herança multipla ou compartilhamento de estado.

Nesse artigo vimos um pouco sobre funções lambda e entendemos um pouco como elas funcionam ! Espero que seja últil, vamos seguir com os próximos artgios, onde vou publicar sobre a API de streams e a API de datas !

Carregando publicação patrocinada...
1
1

Faaaalaaaa GabrielLicar blz ?

Cara muito obrigado pelas palavras fico feliz com esse tipo de coisa valeu mesmo, você não faz ideia de como isso anima e da vontade de escrever mais hehehee.

Pensando nisso também estou colocando todos os MarkDowns crus em um repositório, o desse artigo está aqui fique a vontade, se clonar consegue ver o raw com os códigos do mermaid e etc... ;)

O repositório em questão é esse aqui

E cataloquei os assuntos por diretórios também.

Em adição crieei essa paginazinha no github onde tenho os artigos organizados (ou um pouco menos bagunçados rsrsrsrs) catalogados por assunto e em ordem em que escrevi.

Valeu de mais irmão !

2
1