quinta-feira, 7 de maio de 2015

Java Funcional com Lambdas - Parte 1

Olá pessoal! Nessa série de artigos iremos focar em apresentar as interfaces funcionais do Java 8 e as características que tornam a famosa linguagem orientada a objetos mais próxima do paradigma funcional!


Parte 1 - Introdução às expressões Lambdas
Parte 2 - As interfaces funcionais e as novidades da API Collections
Parte 3 - A classe Stream e conclusão

Introdução às expressões Lambdas


As mudanças presentes no Java 8 são as mais profundas das últimas versões. Junto com essas mudanças temos a adição das expressões lambdas. O objetivo maior é facilitar a criação de implementações de interfaces, evitando assim a grande de quantidade de código quando usamos classes anônimas. Essa funcionalidade, no entanto, vai muito além disso, ela traz características funcionais à plataforma Java e a API Collections foi redefinida para uso mais intensivo das expressões Lambdas.
A necessidade de uso de expressões de código que devem ser executadas após determinada ação é possível no Java através do uso de interfaces cuja implementação deveriam ser informadas por quem usa a API. Um exemplo comum é quando queremos informar qual tarefa uma thread deve executar, fornecemos a implementação da interface Runnable e nela escrevemos o corpo do código run. Vejam que essa abordagem é uma forma orientada a objetos de conseguirmos um comportamento comum em linguagens ditas funcionais.
A introdução de expressões lambdas trouxe também algumas alterações na API básica do Java SE. Foi introduzido um novo pacote java.util.function que traz diversas interfaces com um só método a ser implementado, cujo nome passa a ser interface funcional no Java 8. A das Collections foi também modificada para possibilitar a manipulação de listas através do uso de Lambdas.
O conceito, a sintaxe e as mudanças na API Java para se adequar às expressões Lambdas serão discutidas nesse artigo.


Uso de classes anônimas e as interfaces funcionais

Constantemente vemos na API do Java o uso de interfaces que contém somente um método abstrato, que deve ser implementado pelo usuário de determinada funcionalidade. Um exemplo clássico é com relação às threads, onde temos que implementar a interface Runnable e o método run para informar qual tarefa será executada. Na Listagem 1 você pode ver uma implementação usando a convencional classe anônima do Java. Note que temos que definir toda a assinatura do método e também escrever bastante código só para definirmos nossa classe anônima. Somente o corpo do método run é onde está o código que nos interessa.


Listagem 1. Definindo um Runnable usando uma classe anônima
Runnable r = new Runnable(){
     public void run(){
         System.out.println("Task rodando...");
     }
};

new Thread(r).start();

Outro ótimo exemplo é relacionando a definição de um código que deve ser ativado quando determinado evento acontece. Por exemplo, normalmente temos um método que é executado quando um botão da interface gráfica é clicado. Com o JavaFX tratamos o evento usando uma implementação da interface EventHandler como mostra a Listagem 2, onde temos o registro de um código para execução quando o ponteiro do mouse move sobre o Label.
O Java 8 traz o JavaFX como uma API padrão. As classes da nova biblioteca gráfica da plataforma Java podem ser usadas sem nenhuma biblioteca adicional
Listagem 2. Um ouvinte para o evento MouseMoved de um Label
Label lbl = new Label("Passe o Mouse!");
lbl.setOnMouseMoved(new EventHandler<MouseEvent>(){
     public void handle(MouseEvent e){
          System.out.println("Mouse movendo sob o Label");
     }
});

Por fim, quando queremos ordenar uma lista, temos que criar uma implementação da interface Comparator, onde definimos o nosso critério de comparação para que a ordenação seja feita. Na Listagem 3 temos um exemplo de uma implementação de Comparator para realizar a ordenação de uma lista de Pessoas. O método compare deve ser escrito para determinamos a comparação.


Listagem 3. Criando um Comparator para ordenação de uma lista
Collections.sort(pessoas, new Comparator() {

     public int compare(Pessoa p1, Pessoa p2) {
          return p1.compareTo(p2);
     }

}

O que vemos nas listagens 1, 2 e 3 é o uso de interfaces que contém somente um método abstrato. Na interface Runnable temos o método run, que não recebe nenhum parâmetro e não retornada nada, na interface EventHandler implementamos o método handle que recebe um parâmetro dependendo do tipo genérico da implementação e na implementação do Comparator temos dois parâmetros recebidos e um retorno. No Java 8 chamamos interfaces desse tipo de interfaces funcionais.
Uma outra novidade do Java 8 são as modificações na definição de interfaces, sendo que estas começam a ter suporte a métodos com uma implementação padrão(que podem ser redefinidos pela classe implementadora), métodos estáticos(semelhantes a métodos estáticos em classes) e métodos finais(que não podem ser redefinidos pela classe implementadora) e os clássicos métodos abstratos(que devem ser redefinidos)

Substituindo interfaces funcionais por expressões Lambdas

A expressão Lambda deve ser definida através de uma interface funcional. Veja respectivamente nas listagens 4, 5, 6 como fica a implementação das interfaces Runnable, EventHandler e Comparator usando expressões Lambdas.

Listagem 4. Definindo um Runnable usando uma expressão Lambda
Runnable r = () -> System.out.println("Task rodando...");
new Thread(r).start();

Listagem 5. Definindo um Eventhandler com Lambdas
Label lbl = new Label("Passe o Mouse!");
lbl.setOnMouseMoved(e -> System.out.println("Mouse movendo sob o Label"));
Listagem 6. Um Comparator usando Lambda
Collections.sort(pessoas, (p1,p2)-> p1.compareTo(p2));

Muito mais simples, não? Note que uma expressão Lambda segue o seguinte padrão: Lista de parâmetros -> bloco de código, onde:
  • lista de parâmetros: pode incluir o tipo dos parâmetros, não é obrigatório. O compilador fará inferência de tipo de acordo com a declaração da interface funcional. Quando não temos parâmetros, podemos simplesmente utilizar parênteses () como você pode ver na Listagem 4. Quando há um parâmetro único, não precisamos de parentêses, veja a Listagem 5. Quando há mais de um parâmetro, eles devem ser separados por vírgula, como mostrado na Listagem 6.
  • ->: Esse é o indicar que procede o corpo da expressão Lambda;
  • bloco de código: É onde vai nosso código em sí. Entre chaves você pode escrever o código como se fosse o corpo de um método comum entre chaves {}, veja a Listagem 6. Quando há uma só expressão, podemos omitir as chaves, veja as Listagens 4 e 5.
Na API padrão do Java já há diversos exemplos onde podemos aplicar o que acabamos de aprender, no entanto, algumas adições foram feitas ao Java 8 para maior uso dos Lambdas.

Continuamos na parte 2...

Nenhum comentário:

Postar um comentário