terça-feira, 10 de outubro de 2017

Usando CDI em uma aplicação Java SE

CDI é uma API Java que permite que você delegue as relações entre as classes para um container. Dessa forma você não instancia classes manualmente, mas um container o faz trazendo flexibilidade e performance para a sua aplicação. É também conhecido como uma API de costura entre as camadas de uma aplicação.
No blog Aprendendo JavaEE fizemos uma aplicação muito simples com CDI.

Na versão 2.0 é possível usar CDI em uma aplicação que só tem a API padrão do Java, o chamado Java SE. Nessa postagem vamos mostrar como fazemos isso. 


Configuração do projeto


Vamos usar Maven. No diretório src/main/resources criamos mais um chamado META-INF com um arquivo chamado beans.xml. Esse arquivo permite declarar os beans do CDI, mas vamos deixar ele vazio para que simplesmente o CDI reconheça essa aplicação como uma aplicação CDI.



No pom.xml declaramos só uma dependência, veja:



A aplicação de teste


Vamos criar uma interface muito simples que faz uma saudação:



Imagine que poderíamos ter diversas implementações dela fazendo diversos tipo de saudações, mas vamos criar uma classe de implementação muito simples:



Agora vamos usar a mesma interface em uma classe que não vai ter nenhuma referência para a implementação. Quem vai fazer essa ligação é o CDI. Veja a classe App que usa a Greeter:



O último passo é inicializar o container CDI e pegar uma instância de App para que possamos utilizar, veja:


Ao rodar o código podemos ver que o método foi executado com sucesso:



É isso! Mostramos como se usa CDI em uma aplicação padrão do Java. O código está em nosso github. Um outro recurso para aprender é esse vídeo do Adam Bien (em inglês):

sexta-feira, 5 de maio de 2017

Controlando o arduino a partir do Java usando JArduino

Quando queremos integrar Java com Arduino geralmente utilizamos a porta serial como desmonstrado no livro JavaFX 8: Introduction by Example no capítulo sobre Arduino.O problema com essa abordagem é que teremos que programar em duas linguagens e ainda definir a forma de comunicação.

Outra possibilidade é usar JArduino, que possibilita o uso de código Java para diretamente interagir com o Arduino. O que você precisa fazer é seguir o tutorial do README da página do JArduino. Uma vez usando Java podemos utilizar qualquer API já bem conhecida

Problemas




O seguinte erro me tomou alguns minutos:

java.lang.UnsatisfiedLinkError: no rxtxSerial64 in java.library.path thrown while loading gnu.io.RXTXCommDriver

Ele vem por causa da falta da API nativa RxTx. Simplesmente baixe a API para o seu sistema aqui aqui e colocou na lib da sua JDK. No meu caso o diretório que usei foi: /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64



Lendo um sensor de luz (LDR) a partir do Java

 

O "olá mundo" do mundo arduino é fazer um led piscar. Eu queria começar com algo mais interativo, então criei uma aplicação simples que lê uma entrada analógica conectada a um LDR, como descrito nesse tutorial. Coisas para se notar sobre o código



  • A classe JArduino imita um código Arduino. Você simplesmente estende a classe JArduino e implementa suas coisas no método loop e configura as portas no método setup;
  • Eu estou usando maven e tive que adicionar alguns repositórios no meu pom.xml pois algumas dependências não estão disponíveis na central do Maven;
  • A única coisa que precisamos fazer no lado do arduino é subir o código provido pelo JArduino. Lembre-se de usar a porta certa na sua classe;
  •  O circuito é o mesmo do tutorial Using an LDR Sensor with Arduino: A Tutorial for Beginners, a diferença aqui é que uso o Garagino:

Aqui está o código:

O resultado é simplesmente a intensidade da luz no console:




Agora em breve no blog Aprendendo JavaFX iremos postar algo visual. Até mais!

quarta-feira, 28 de dezembro de 2016

Organizar execução de threads no Java com join

Nessa outra postagem rápida nós vamos mostrar como organizar a execução de Threads no java com join.

Para entender o ponto dessa postagem, imagine que várias threads estão fazendo um processamento e você vai querer somente o valor mais atual desse processamento ou disparar uma ação depois que todas as threads tenham terminado. Bem, uma forma muito simples é usar join, que vai parar a thread atual até que a thread iniciada tenha concluído a execução.

Claro que no exemplo que vamos mostrar isso não é muito útil, pois as threads estão sendo executados de forma sequencial! Mas o exemplo ser para ilustrar como pode ser utilizado esse método. Com certeza que você ao conhecer esse método, fará uso mais criativo e úteis!

Vamos direto ao ponto, então temos a aplicação abaixo onde diversas threads atualizam o mesmo mapa com o tempo em milis atual e queremos saber o valor mais recente.


O código da classe AtualizadorMapa vai dar xabu sim ou com certeza?
Vai sim, terems um resultado como o seguinte:

Thread 1 - 1482988764969
Último valor do mapa: null
Thread 3 - 1482988764969
Thread 2 - 1482988764969
Thread 0 - 1482988764969
Thread 4 - 1482988764969

As vezes vai funcionar. Sim, as vezes as coisas vão sair no lugar, mas nem sempre. Agora imagine a dor de cabeça que implementações concorrentes não dão em produção!

Uma forma simples (inútil, diriam alguns) de resolver é usando o join. Talvez a única vantagem seja, mesmo perdendo a execução paralela, o fato que se uma thread lançar exceção, o problema principal não é afetado e outras threads poderão executar em seguida. Mas lembrem-se, o que temos aqui é um exemplo simples, há muitos possíveis para o join e meu objetivo hoje é somente apresentar esse método. Veja agora o código da classe AtualizadorMapaSincronizadoComJoin e notem que adicionar um join logo após iniciarmos a thread que criamos. Isso garante que a thread principal, a thread da classe, só vai continuar depois que nossa thread terminar o processamento. Como resultado, temos sempre o valor mais atual no mapa após o for...

O nosso objetivo aqui foi somente apresentar esse interessante método. Ah, se você ficou perdido com a forma que declaramos o Runnable ali, veja nosso artigo sobre Java Funcional com Lambdas.

terça-feira, 17 de novembro de 2015

Lendo e escrevendo arquivos usando Java 8

Ler arquivo em Java já foi um pouco complicado, pois o desenvolvedor tinha que ter conhecimento da API do Java I/O (InputStream, BufferedReader e por aí vai). Nessa postagem breve vou mostrar algumas formas incrivelmente fáceis de lidar com arquivos no Java 8.


Paths e Files


Essas classes contém métodos estáticos que permitem lidar com arquivos de forma mais fácil.
  • Paths contém só dois métodos que permitem converter uma String (ou uma URI) com o caminho de um arquivo(ou de um diretório) em um objeto do tipo Path, que representa um caminho no sistema operacional. Notem que Files usa o objeto Path em muitos de seus métodos;
  • Files é uma classe cheia de métodos utilitários para trabalharmos com arquivo e com o sistema de arquivos em Java.

Agora vamos  mostrar simples exemplos de como abrir arquivos. Se você usa Java 8, vai ver como está mais do que simples lidar com arquivos.

1) Ler os bytes do arquivo


Ler os bytes de um arquivo é essencial se quisermos trabalhar com imagens e outros tipos de arquivos. Mas também é util se queremos passar o conteúdo de um arquivo texto para uma String. Veja:


byte[] bytesArquivo = Files.readAllBytes(p);
String textoArquivo = new String(bytesArquivo);
System.out.println(textoArquivo);

2) Ler as linhas de um arquivo em uma lista


Caso você queria ter linha a linha de um arquivo sem ter que ler ele inteiro para "picar" em linhas, você pode! Simplesmente use o método Files.getAllLines passando o Path do arquivo e salve o retorno em uma lista(java.util.List):


List<String> linhasArquivo = Files.readAllLines(p);
for (String l : linhasArquivo) {
    System.out.println("Linha: " + l); 
}  
3) Ler as linhas de um arquivo em um Stream

Já falamos aqui das facilidades de um Stream e com uma linha de código a gente pode passar todas as linhas de dentro um arquivo de texto para um java.util.Stream, e nele fazer transformações, filtros, etc. Veja como é fácil:

Files.lines(p).forEach(System.out::println);

O que o método acima faz é pegar linha a linha de um arquivo e chamar o método System.out.println. Note que não preciso chamar o método em sí, mas sim passar a referência.


4) Escrever em um arquivo


Escrever em um arquivo é também assunto de chamar um método só! Chamados o método Files.write e passamos os bytes que queremos escrever:

String linhaNova = "Linha criada em " + new java.util.Date().toString();
Files.write(p, linhaNova.getBytes());

Conclusão

 
Mostramos as facilidades de lidar com arquivo no Java 8. Vejam o código completo baixo. Ele está pronto para ser compilado com javac e executado com java, mas lembre-se de criar um arquivo dados.txt no mesmo diretório onde estará o seu .class


domingo, 10 de maio de 2015

Java Funcional com Lambdas - Parte 3 (Stream)


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 

A classe Stream

A API Collections foi adicionada ao Java desde a versão 1.2 da JDK e desde então muitas melhorias foram feitas. Para o Java 8, a maior novidade é que o tratamento de listas podem ser dar através de uma classe chamada Stream.
Localizada no novo pacote java.util.stream, Stream representa uma sequência de dados de um dado tipo. À essa sequência de dados, podemos aplicar diversas operações, tais como: filtrar, transformar os elementos para outros tipos, acumular ações sobre cada elemento, entre outros. O Stream é parametrizado através de genéricos, ou seja, eles têm um tipo e quando pegamos o stream de uma lista, os elementos do Stream terão também aquele tipo. Por outro lado, temos classes especializadas, tais como IntStream, LongStream e DoubleStream, que permitem que possamos ter funções mais específicas para esses tipos.
As funções aplicadas em um stream podem ser intermediárias ou terminais. As intermediárias permitem que outras funções ainda sejam aplicadas, sempre retornando um stream para processamento, e as terminais fecham os streams, não possibilitando aplicar novas funções. Quase todas fazem uso das interfaces funcionais descritas anteriormente. Destacamos as seguintes operações intermediárias de um Stream:
filter
Essa operação recebe uma implementação da interface Predicate, onde o tipo de parâmetro recebido é do tipo do Stream e a função deve retornar um boolean. Serve para filtrar elementos do stream de acordo com condições que o programador determina.
peek
A operação peek recebe uma implementação da interface Consumer onde o elemento do stream é passado para processamento e podemos utilizar o mesmo em algum processamento adicional.
map
Com map podemos transformar o stream de um tipo para outro. Para fazermos isso, a operação recebe uma implementação da interface do tipo Function, que deve receber o elemento do stream de um dado tipo e retornar um elemento para compor um novo stream do tipo desse retorno. Também temos as funções mapToInt, mapToLong e mapToDouble, onde o tipo de retorno é um Stream, como o nome da função diz, respectivamente dos tipos: int, long e double.

No caso de operações terminais, destacamos as seguintes:
count
Essa é uma operação terminal e ao chamar a mesma temos um retorno do tipo long que representa a quantidade de elementos que há no stream.
forEach
É uma função terminal semelhante à peek, onde temos a execução de um Consumer para cada elemento do stream.
toArray
Como o nome diz, essa operação retorna uma lista simples como os elementos do Stream. Note que o Array não irá conter elementos do tipo do stream, mas sim elementos do tipo Object.
reduce, max e min
A operação reduce utiliza uma interface do tipo BinaryOperator para reduzir os elementos do stream. As operações max e min são tipo particulares de operações que realizam a redução dos elementos dado um Comparator. O retorno é um objeto do tipo Optional. Há também as funções de redução para os Streams especializados, como sum, que soma todos os elementos de um IntStream, DoubleStream ou LongStream.
A interface Optional também foi adicionada à versão 8 da JDK e funciona como um contêiner para valores de um dado tipo. O objetivo é levar o programador a realizar um tipo de programação que evite a famigerada exceção NullPointerException. Com ela, podemos mais facilmente lidar com operações que podem resultar em nulo, evitando código “perigoso” que geralmente causam problemas inesperados em tempo de execução.
É ainda possível realizar transformações mais complexas dos elementos da Stream usando a função collect, que recebe um objeto do tipo java.util.stream.Collector, um interface que contém diversos métodos utilizatários para uso.
Para entermos melhor tudo que foi falado sobre stream até agora, vamos ver como ficaria o código da Listagem 7 quando usando lambdas e as novas características da API Collections na Listagem 11. Notem que a partir da lista de produtos, temos um stream e dele filtramos, aplicamos uma função usando o peek, ou seja, ainda temos o stream para usar, então, realizamos o mapeamento para Long, gerando um novo stream com que representa a quantidade de produtos no estoque, por fim invocamos a função terminal sum, aí temos a soma de todos os elementos que geramos após o mapeamento para long. Em seguidas, pegamos novamente o stream da lista, mas simplesmente filtramos os que são feitos antes de 2014 para então invocar a função processaAntigos. Por fim, reduzimos a lista a um Optional usando a função min e passando um Comparator da data de criação.


Listagem 11. Aplicando operações na lista de produtos usando a classe Steam
long somaProdutosMarcaSuper = produtos.stream()
     .filter(p -> p.qtdeEstoque > 5 && p.marca.equals("Super") && p.dataCriacao.get(ChronoField.YEAR) == 2014)
     .peek(p -> processaProdutoEmExcesso(p))
     .mapToLong(p -> p.qtdeEstoque).sum();
produtos.stream()
     .filter(p -> p.dataCriacao.get(ChronoField.YEAR) < 2000).forEach(p -> processaProdutoAntigo(p));
Optional maisAntigo = produtos.stream().min((p1, p2) -> p1.dataCriacao.compareTo(p2.dataCriacao));


Uma característica nova do Java 8 e que potencializa ainda mais o uso de Stream ao lidar com listas, é a possibilidade de usar referências a métodos. Através de :: (dois pontos), podemos eliminar código repetido ao tratar de listas com stream. Você pode passar a referência de qualquer método invés de ter que escrever a expressão Lambda inteira só para chamar o método. Ao fazer isso, a referência do método deve ser compatível com os parâmetros que se deseja receber e o retorno. Por exemplo, caso quisermos imprimir os elementos de um stream ao usar forEach, invés de fazer uma expressão para invocar System.out.println, podemos simplesmente passar uma referência para esse método: lista.forEach(System.out::println).
A Listagem 12 mostra a Listagem 11 reescrita usando referências a métodos. Vejam que ao chamar peek, invés de escrever toda a expressão Lambda, passamos a referência do nosso método estático e também usamos o método de acesso ao campo qtdeEstoque quando realizando o mapeamento para long.

Listagem 12. Aplicando operações na lista de produtos usando a classe Steam e referências de métodos
long somaProdutosMarcaSuper = produtos.stream()
     .filter(p -> p.qtdeEstoque > 5 && p.marca.equals("Super") && p.dataCriacao.get(ChronoField.YEAR) == 2014) 
     .peek(ProcessaProdutosRef::processaProdutoEmExcesso)
     .mapToLong(Produto::getQtdeEstoque).sum();
produtos.stream()
     .filter(p -> p.dataCriacao.get(ChronoField.YEAR) < 2000)
     .forEach(ProcessaProdutosRef::processaProdutoAntigo);

Conclusões

Nesse artigo foi feita a introdução das expressões Lambdas e como ela afeta a API base do Java 8. Essa novidade trouxe muitos benefícios para o desenvolvedor Java e renova a linguagem sem perder o que já temos estabelecido. Quem já usa JavaFX, trabalha com a API de Collections e outras APIs do Java, verá bastante uso das expressões Lambdas no seu dia a dia.
Para se especializar nesse novo mundo, a prática é necessário. Tentar reescrever código antigo usando expressões Lambdas e ler a documentação oficial irá acelerar ao leitor a familiarização com todas as novidades que vêm no versão 8 do Java. 
Para se aprofundar no tema veja os seguintes links:

Página da OpenJDK sobre as expressões Lambdas
http://openjdk.java.net/projects/lambda/
Site oficial da JSR 335 –Lambda Expressions for the JavaTM Programming Language
jcp.org/en/jsr/detail?id=335

sexta-feira, 8 de maio de 2015

Java Funcional com Lambdas - Parte 2 (Mudanças na API de Collections)

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 

As interfaces funcionais e as novidades da API Collections



As novas interfaces funcionais do Java 8

Os especialistas nas expressões Lambdas adicionaram ao Java 8 um pacote de interfaces funcionais muito úteis quando focando no modelo de programação voltado a essas expressões. Com o uso de genéricos e interfaces funcionais, foram definidas diversas interfaces que podem ser usadas pelo programador quando criando seu próprio código ou em designers de APIs. No pacote java.util.function, você encontra diversas interfaces funcionais, entre elas destamos:
Predicate
É uma interface funcional para testes booleanos. O método test é usado para realizar um teste booleano com os atributos de um objeto passado como parâmetro. É possível utilizar os métodos a and e ou para combinar diversos resultados booleanos. A interface é parametrizada através de generics, sendo que o parâmetro vai determinar o tipo de objeto a ser recebido no método test.
Function
Executa a função sobre um objeto T retornando um objeto do tipo R. Essa interface contém somente o método apply, que recebe T para retornar R. É possível usar o método andThen para configurar uma função a ser aplicada após que aplicamos essa e o método compose para configurarmos uma função que será aplicada antes dessa.
Consumer
Simplesmente aplica uma ação sobre o objeto de tipo T através do método apply. Similarmente à interface Function, podemos usar um método andThen para configurar um Consumer que deve ser chamado após a execução desse.
BinaryOperator
Essa interface funcional recebe dois objetos do tipo T e retorna um objeto do tipo T. Podemos utilizar os métodos estáticos maxBy e minBy para que tenhamos um implementação dessa função que calcula o máximo e o mínimo de acordo com o Comparator passado para esses métodos.
Supplier
Cria um objeto do tipo T. O método get é chamado e deve retornar um objeto do tipo T. Importante deixar claro que a interface funcional em sí não necessariamente obriga quem está implementando a criar um objeto, mas sim retornar um do tipo T, ficando a cargo de quem implementa como deveria ser realizada a criação do objeto.

As interfaces funcionais são parametrizadas através do uso de generics, muitas vezes determinando os parâmetros que as funções recebem. O uso dessas funções fica mais evidente nos exemplos a seguir, onde apresentamos o uso de Lambdas na API Collections do Java.

As novidades da API Collections

As collections, ou coleções, sofreram mudanças significativas no decorrer dos anos: foram parametrizadas através de Generics, ganharam novas classes e uma sintaxe mais agradável para iterarmos sobre os elementos de uma das classes da API de collections.
No entanto, uma manipulação dos elementos de uma lista ainda acontece através da escrita de muito código e pouco intuitiva. Por exemplo, digamos que temos uma lista de objetos do tipo Produto com os atributos nome, data de criação, quantidade em estoque e marca. Há uma aplicação que lista esses produtos. Surge um requerimento para tratamento de produtos em excesso de uma determinada marca: produtos que tiverem mais que 5 unidades da marca Super e criados no ano de 2014 devem ser submetidos a um processamento através do método processaProdutoEmExcesso e também devemos somar a quantidade de todos produtos que estiverem nessa condição. Já produtos antigos, fabricados antes de 2000, devem ser processados usando o método processaProdutoAntigo, além de informar o mais antigo produto de todos. Utilizando a maneira convencional, temos o código na Listagem 7, onde fazemos uso de loops e verificação manual de cada elemento. Notem como o código é inflexível e nos leva ao uso de diversos comandos para atingir um objetivo simples.

Listagem 7. Aplicando determinadas ações sob uma lista
List<Produto> produtos = todosProdutos();
     for (Produto p : produtos){
          if(p.qtdeEstoque > 5 && p.marca.equals("Super") && p.dataCriacao.get(ChronoField.YEAR) == 2014){
               processaProdutoEmExcesso(p);
               somaProdutosMarcaSuper+=p.qtdeEstoque;
          }
          if(p.dataCriacao.get(ChronoField.YEAR) < 2000){
               processaProdutoAntigo(p);
               if(p.dataCriacao.isBefore(maisAntigo.dataCriacao)) maisAntigo = p;
          }
}

Com o tempo, diversas necessidade de filtragem desse relatório aparecem e temos que flexibilizar o mesmo. Aí modificamos o método acima para flexibilizar a aplicação de uma condição e a execução de uma ação como mostrado na Listagem 8, onde fazemos uso das interfaces funcionais Predicate e Consumer, descritas anteriormente. A chamada desses métodos usando a forma tradicional, com classes internas é verbosa e pode ser vista na Listagem 9 e a forma melhorada, com Lambdas, está na Listagem 10.

Listagem 8. Melhorando o código anterior para flexibilizar a condição de filtragem da lista
public static void realizaProcessamentoSobCondicao(List<Produto> produtos, Predicate<Produto> condicao, Consumer<Produto> acao){
     for(Produto p : produtos){
          if(condicao.test(p)){
               acao.accept(p);
          }
     }
}

Listagem 9. Utilizando método realizaProcessamentoSobCondicao usando Classes Anônimas
realizaProcessamentoSobCondicao(produtos, 
     new Predicate<Produto>(){
         public boolean test(Produto p){
               return p.qtdeEstoque > 5 && p.marca.equals("Super") && p.dataCriacao.get(ChronoField.YEAR) == 2014;
     }
},
     new Consumer<Produto>(){
          public void accept(Produto p){
               processaProdutoEmExcesso(p);
               somaProdutosMarcaSuper+=p.qtdeEstoque;
          }
     }
);

realizaProcessamentoSobCondicao(produtos,
     new Predicate<Produto>(){
          public boolean test(Produto p){
               return p.dataCriacao.get(ChronoField.YEAR) < 2000;
          }
     },
     new Consumer<Produto>(){
          public void accept(Produto p){
               processaProdutoAntigo(p);
               if(p.dataCriacao.isBefore(maisAntigo.dataCriacao)) maisAntigo = p;
          }
     }
);

Listagem 10. Utilizando método realizaProcessamentoSobCondicao usando expressões Lambdas
realizaProcessamentoSobCondicao(produtos,
     p -> p.qtdeEstoque > 5 && p.marca.equals("Super") && p.dataCriacao.get(ChronoField.YEAR) == 2014,
     p ->{
          processaProdutoEmExcesso(p);
          somaProdutosMarcaSuper+=p.qtdeEstoque;
     }
);

realizaProcessamentoSobCondicao(produtos,
     p -> p.dataCriacao.get(ChronoField.YEAR) < 2000,
     p -> {
          processaProdutoAntigo(p);
          if(p.dataCriacao.isBefore(maisAntigo.dataCriacao)) maisAntigo = p;
     }
);

Nosso código, no entanto, ainda está muito verboso e falta flexibilidade, pois a cada programa que criarmos, temos que criar novos métodos semelhantes ao realizaProcessamentoSobCondicao, mostrado na Listagem 8. Não seria interessante já ter algo semelhante a isso disponível na API nativamente?
A API Collections da versão 8 do Java traz uma forma interessante de tratarmos listas fortemente baseado nas expressões Lambdas e com uso das interfaces funcionais já descritas. O código mostrado acima poderia ser reescrito em uma linha e ter a leitura do mesmo muito facilitada. A classe que possibilita o uso extensivo de Lambdas e potencializa o uso de coleções no Java é a classe Stream.

Até mais na parte 3 onde falamos da classe Stream...

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...