Chegando ao Lambda no Java 8

Propaganda
_lambda
Chegando ao
Lambda
no Java 8
Como melhorar seu código atual (e futuro)
utilizando funções Lambda.
Você sabe ordenar uma lista? Sabe selecionar os elementos de uma
lista a partir de um critério? E extrair os valores de uma determinada propriedade do objeto de uma lista?
São tarefas comuns que estamos cansados de repeti-las. Mas será
que conhecemos a melhor forma de escrevê-las? Há como fazer de
forma mais concisa, sem perder a legibilidade?
Neste artigo vamos mostrar o que muda nessas tarefas do dia a dia
ao utilizar bibliotecas como o Google Guava e LambdaJ, que adicionam um sabor funcional ao Java atual. Depois chegaremos ao
futuro Java 8, para ver como o lambda e os defender methods estão
mudando a linguagem em direção ao funcional.
Paulo Silveira | [email protected]
Bacharel e mestre em computação pela Universidade de São Paulo. Fundador do GUJ.com.br, trabalha com Java há 11 anos. É um
dos responsáveis técnicos pelos cursos da Caelum.
Raphael Lacerda | [email protected]
Pós-graduado em Engenharia de Sistemas. Atua como analista de sistemas no Banco do Brasil e ministra cursos na Caelum Brasília. Entusiasta por expressividade de código e DSLs. Possui as certificações SCJP e SCWCD. É um dos responsáveis técnicos pelos
cursos da Caelum.
37 \
P
rogramação funcional é um tema em alta mesmo
no mercado corporativo. Sua ascensão ficou bem
apresentada pelo artigo de Steve Vinosky, “Welcome
to the functional web”, e pode ser claramente vista pela grande quantidade de linguagens funcionais
(ou ao menos com um pouco mais de características
funcionais) na JVM. Scala e Clojure são expoentes
aqui. Os artigos na MundoJ revelam também essa
tendência.
Java é orientada a objetos. O que poderíamos
aproveitar do conceito do paradigma funcional para
melhorar alguns pontos subjetivos do nosso código:
legibilidade, expressividade e concisão? Usando apenas a API padrão, muito pouco. Ficamos reféns das
longas e “verborrágicas” classes anônimas, ou de laços que se repetem exaustivamente.
Como o Java e o JCP estão reagindo a essa tendência? Certamente a linguagem está bastante defasada, não só em relação às outras com pequena
penetração no mercado corporativo, como Scala e
Clojure, mas também do Ruby e Python, sem contar
o C#, que já possui recursos funcionais muito avançados há bastante tempo.
Para a oitava edição da plataforma, prevista para
setembro de 2013, teremos a inclusão de uma sintaxe própria para definir lambdas, em estilo muito
similar a essas linguagens. Veremos, neste artigo,
como será essa nova sintaxe, junto com algumas novidades da JSR 335.
Mas precisamos esperar ainda um ano para tirar
proveito de uma API mais elaborada, além da sintaxe
adocicada?
Ao final deste artigo você conhecerá duas bibliotecas funcionais: Guava e LambdaJ. Elas possuem
uma robusta API para facilitar o trabalho do dia a
dia, em especial relacionado a coleções. O LambdaJ
vai além: abusa das proxies dinâmicas para mimetizar uma sintaxe diferente.
Você pode ler este artigo de maneira relativamente independente: mostraremos cada uma das
abordagens para resolver problemas parecidos, envolvendo simples manipulações de coleções, utilizando a classe Palestrante. A Listagem 1 mostra essa
classe, que implementa Comparable de forma trivial
e possui, como atributos, o número de posts do palestrante no GUJ, um id e um nome.
Listagem 1. Implementação de Palestra e Palestran-
te.
public class Palestrante implements
Comparable<Palestrante> {
private final int
id;
private final String
name;
private final int
postsNoGuj;
private boolean
favoritado;
/ 38
}
public void favoritar() {
favoritado = true;
}
@Override
public int compareTo(Palestrante o) {
Objects.requireNonNull(o);
return Integer.compare(this.id, o.getId());
}
Algumas tarefas do dia a dia com o
Java sem esteróides
Vamos atacar os problemas triviais sem utilizar
nenhuma biblioteca de fora do Java. Se temos uma
lista de Palestrante chamada palestrantes, como
costumamos imprimir todos seus nomes? Escrevemos um foreach:
for (Palestrante p : palestrantes) {
System.out.println(p);
}
Para somar todos seus posts, temos outro simples laço:
int soma = 0;
for (Palestrante p : palestrantes) {
soma += p.getPostsNoGuj();
}
Para filtrar todos os que já fizeram mais de 500
posts no GUJ, e depois favoritá-los, o código também
é bastante simples:
for (Palestrante p : palestrantes) {
if (p.getPostsNoGuj() > 500)
p.favoritar();
}
Para orderná-los através de posts, criamos um
Comparator. Podemos fazer isso em outra classe,
mas é comum ficar numa classe anônima:
Comparator<Palestrante> comparador = new
Comparator<Palestrante>() {
public int compare(Palestrante o1, Palestrante o2) {
if (o1.getPostsNoGuj() < o2.getPostsNoGuj())
return -1;
if (o1.getPostsNoGuj() > o2.getPostsNoGuj())
return 1;
return 0;
}
};
Collections.sort(palestrantes, comparador);
São quatro curtos códigos que estamos cansados
de escrever no dia a dia. Como eles ficam adicionando um pouco de programação funcional? Será que
torná-los curtos facilitaria a legibilidade? Melhoraria a forma de pensar?
Google Guava
O Guava é um apanhado geral de funcionalidades
que os desenvolvedores do Google usam nos projetos
Java da empresa. A biblioteca abrange várias áreas
como coleções (inclusive criando outros tipos de collections como o MultiSet), concorrência, I/O, reflection, validação, dentre outros. Eles resolveram juntar
todos esses utilitários em um projeto só, dando origem ao Guava. Está disponível para quem faz gerenciamento de dependências com o Maven, Gradle, Ivy
e Buildr e tem uma boa documentação.
A própria biblioteca diz que para tentar atingir
uma programação funcional utilizando Java 7 é só
através de um código estranho e muito verboso, basicamente utilizando classes anônimas, portanto ela
traz funcionalidades para reverter esse quadro.
O projeto contém algumas classes e interfaces
que ajudam na programação no estilo funcional como
Function, Predicate, FluentIterable e uma classe utilitária Collections2. Em suma, podemos fazer uma
operação básica como filtragem com praticamente
uma combinação de todas elas, o que muda é que, por
exemplo, a classe FluentIterable provê essa funcionalidade seguindo o padrão FluentInterface.
Como primeiro exemplo, a partir de uma lista de
participantes, deve-se obter uma lista dos nomes em
maiúsculo. Para isso vamos utilizar os métodos da
classe FluentIterable from e trasform.
System.out.println(FluentIterable.from(palestrantes)
.transform(new Function<Palestrante, String>() {
@Override
public String apply(Palestrante palestrante) {
return palestrante.getName().toUpperCase();
}
}));
Comparators. Ela possui alguns métodos utilitários
para ordenação, como levar em conta valores null,
ordem reversa, ordem lexicográfica, verificar se a lista
está ordenada, dentre outros. Vamos fazer três exemplos rápidos de ordenação. Primeiramente ordenar
uma lista desordenada de participantes pela sua ordem natural (definida pela interface Comparable).
List<Palestrantes> palestrantesOrdenadosPorId =
Ordering.natural().sortedCopy(palestrantesDesordenados);
Depois vamos ordenar por nome, para isso vamos
criar utilizar a classe Function.
Function<Palestrante, String> ordernacaoPorNome = new
Function<Palestrante, String>() {
@Override
public String apply(Palestrante p) {
return p.getName();
}};
List<Palestrante> palestrantes =Ordering.natural().
onResultOf(ordernacaoPorNome).
sortedCopy(palestrantes);
Por fim ordená-los por nome e posts no guj, onde
nomeComparator e postsComparator representam
referências para classes anônimas que implementam
a interface Comparator.
List<Palestrante> palestrantesNomePosts =
Ordering.from(nomeComparator).compound(
postsComparator).sortedCopy(palestrantes);
Concluindo, a principal vantagem do Guava em
relação à API nativa do Java é que ele possui alguns
métodos de suporte mais efetivos, entretanto criar
classes anônimas Function e Predicate não muda
muito em quantidade/expressividade de código.
Agora vamos para um problema mais comum. A LambdaJ
LambdaJ é uma bibioteca para manipular colepartir de uma lista de palestrantes, obter somente
ções
de uma forma mais simples e natural. Foi criada
aqueles que tiveram mais do que 500 posts.
por Mario Fusco e surgiu devido à necessidade de enSystem.out.println(FluentIterable.from(palestrantes).filter(
tender melhor uma parte do domínio que lidava com
new Predicate<Palestrante>() {
interações em coleções em um projeto que ele parti@Override
cipava. A biblioteca foi desenhada como sendo uma
public boolean apply(Palestrante palestrante) {
DSL interna para o Java. Segundo Fusco, esse código
return palestrante.getPostsNoGuj() > 500;
era complexo, repetitivo e de difícil leitura. Os desen}
volvedores gastavam mais tempo tentando adivinhar
}));
o que um determinado loop fazia do que escrevendoExtraindo métodos e utilizando imports estáti- -o. E ele afirma: “...códigos como este são feitos para
o computador ler e não o programador...”. Bom, a sicos, conseguimos um código mais sucinto:
tuação parece familiar para você?
Ela é muito fácil de se utilizar, tem uma boa doSystem.out.println(from(palestrantes).filter(
comPostsMaiorQue500()));
cumentação e basta ir ao site do projeto para baixá-la, inclusive já possui um jar único com todas as dePara fazer funções de ordenação, vamos usar a pendências e está disponível também para quem faz
classe Ordering, que é um comparador fluente e pode o gerenciamento com o Maven. Na parte técnica, ela
ser usado para manipular, estender e fazer uso de faz muito uso de proxy e reflexão, o que a torna mais
39 \
expressiva que as anteriores, todavia um pouco menos eficiente com relação ao desempenho. Recomenda-se verificar na página do projeto a sua análise de
desempenho e a questão toda pode se resumir a um
trade-off, perdendo um pouco no desempenho para
ganhar na expressividade de código. Sua principal
classe é a Lambda, que contém uma série de métodos
(funções) estáticos.
Inicialmente podemos imprimir o nome de todos
os participantes, para isso vamos utilizar a função
joinFrom.
Podemos fazer um pequeno refactoring para torná-lo mais legível:
List<String> nomesMaiusculos =
convert(nomeDosPalestrantes, paraMaiusculo());
Temos agora o seguinte problema, mais comum
no dia a dia do desenvolvedor. A partir de uma lista
de palestrantes, selecionar os palestrantes com mais
de 500 posts e colocá-los como favoritos. Já sabemos
como é essa solução usando Java puro, ocupando algumas linhas de código. Já com LambdaJ tudo se resolve com apenas uma linha. Para isso vamos utilizar
System.out.println(joinFrom(palestrantes).getName());
o método select para selecionar os participantes e o
método forEach para percorrer essa lista e favoritáO import estático do método joinFrom foi feito -los. Também iremos usar o método having e greapara facilitar a leitura. Agora vamos ordenar os par- terThanOrEqualTo, ambos do Hamcrest, uma depenticipantes a partir da quantidade de posts, para isso dência do LambdaJ.
vamos utilizar o método sort.
List<Palestrante> participantesOrdenadosPorPost =
sort(palestrantes, on(Palestrante.class).
getPostsNoGuj());
forEach(select(palestrantes,having(on(
Palestrante.class).getPostsNoGuj(),
greaterThanOrEqualTo(500)))).favoritar();
Poderia ter sido usado o método and e or para
adicionar outras restrições. Agora a leitura ficou fáO item de comparação é obtido a partir do métocil, todavia ainda pode ser melhorada, tornar-se mais
do on. Podemos obter uma expressividade melhor ao
expressiva. Vamos extrair a parte de comparação para
extrair a comparação para um método privado.
um método privado e parametrizar o valor a ser comparado.
List<Palestrante> participantesOrdenadosPorPost =
sort(palestrantes, porPostsNoGuj());
A classe Lambda possui método de agregação
como avg, max e min. Exemplo:
forEach(select(palestrantes,
comPostsMaiorQue(500))).favoritar();
O LambdaJ também possui uma classe LambdaCollection que segue o padrão Fluent Interface assim
como o FluentIterable do Guava. Vamos utilizá-la
no seguinte problema: tendo uma lista de palestras
Agora a partir de uma lista de participantes, va- (considere a nova classe Palestra abaixo), selecionar
mos colocar todos os nomes em maiúsculo. Primeiro as palestras que tiveram uma votação superior a 100,
vamos extrair o nome dos participantes. Para isso, então a partir desta selecionar os seus palestrantes
ordenados por quantidade de posts. Para isso vamos
será usado o método extract.
utilizar os métodos with, que devolve uma referência
List<String> nomesDosPalestrantes =
para uma LambdaCollection, retain, que tem funcioextract(palestrantes, on(Palestrante.class).
namento análogo ao select e o extract que já vimos
getName());
previamente:
int totalDePosts = sumFrom(palestrantes).
getPostsNoGuj();
Com o nome dos participantes, o objetivo agora é
colocá-los em letra maiúscula. Para isso vamos usar o
método convert e a interface do LambdaJ Converter.
Ela especifica como converter um objeto de um tipo
em outro. No exemplo será de String para String.
List<String> nomesMaiusculos = convert(
nomeDosPalestrantes, new Converter<String,
String>(){
@Override
public String convert(String nome){
return nome.toUpperCase();
}});
/ 40
public class Palestra {
private final String titulo;
private final Palestrante palestrante;
private final int quantidadeDeVotos;
// getters e constructor omitidos
}
List<Palestrante> palestrantes = with(palestras).
retain(having(on(Palestra.class).
getQuantidadeDeVotos(),
greaterThanOrEqualTo(100))).
extract(on(Palestra.class).getPalestrante()).
sort(on(Palestrante.class).getPostsNoGuj());
mais programação funcional na MundoJ/
> Na edição número 52 da MundoJ, Bruno Kioshita fez uma excelente introdução sobre paradigma funcional, explicando conceitos
como Functions, Predicates, dentre outros. Também abordou como fazer o uso da biblioteca Apache Functor, que possui ideias
parecidas com as bibliotecas que serão descritas neste artigo. Por isso, iremos focar essencialmente na prática de como utilizar funções
em Java, atualmente com bibliotecas externas e futuramente com o Lambda no Java 8.
> Já na edição 53, Leandro Moreira fez um artigo de introdução à linguagem Clojure, remetendo a alguns conceitos de Programação
Funcional. Nesta mesma edição Hugo Ferreira descreve vários problemas que podem ser resolvidos com programação Objeto-Funcional
e, por fim, Rafael Ferreira traz o recurso de pattern matching da linguagem funcional Scala.
Novamente, com uma refatoração, temos:
ter sido adicionado. E no Java 8 ele foi, dentro da interface Iterable.
List<Palestrante> palestrantes = with(palestras).
Isso é bastante perigoso: evoluir uma interface
retain(comQuantidadeDeVotosMaiorQue(100)).
adicionando novos métodos quebram classes exisextract(palestrante()).sort(porPostsNoGuj());
tentes que a implementavam! Como o time fez isso,
Por fim, recomendamos olhar nas referências minimizando os riscos? Eles adicionaram suporte aos
para verificar as outras funcionalidades da biblioteca, extension methods. Se você olhar o código-fonte da
alguns métodos como index, group e project podem interface Iterable, encontrará o seguinte método:
resolver problemas de uma forma mais concisa do
void forEach(Block<? super T> block) default {
que a sua atual solução. Por exemplo, em um cenário
Iterables.forEach(this, block);
de aplicações distribuídas, em que há a necessidade
}
de se serializar objetos para transferi-los entre JVMs
diferentes, o padrão DTO pode ser utilizado. Então
Agora você pode criar métodos em interfaces no
podemos chegar ao seguinte código, caso queiramos Java e colocar uma implementação default, desoinstanciar objetos do tipo DadosPessoais que tam- brigando suas implementadoras de o reescreverem.
bém possuam o atributo nome:
Algo parecido com os mixins do Ruby e com os traits
do Scala, mas ainda sem a possibilidade de conter
List<DadosPessoais> palestrantesParaTransferencia =
atributos não-estáticos. A palavra-chave default,
project(palestrantes,DadosPessoais.
dentro de um método de interface, é quem faz essa
class,on(Palestrante.class).
mágica e define-o como extension method. Classes
getId(),on(Palestrante.class).getName());
que implementam Iterable não precisarão reescrever
esse método.
Há muitas interfaces e classes novas no Java 8 nos
Lambda no Java 8
pacotes lang e util. Iterables, Comparators, MapStreApesar de estar planejado apenas para setembro
am, BiVal/BiValue são apenas algumas delas, além do
de 2013, você já pode utilizar milestones do JDK8,
pacote java.util.functions, onde a Block se encontra.
disponíveis para os principais sistemas operacionais,
Trabalharemos com elas no nosso dia a dia e vamos
inclusive para o Mac OSX. O time do lambda do JDK
utilizar algumas delas nessa seção.
8 é liderado por Brian Goetz, muito conhecido por
seu livro de programação concorrente na plataforma
palestrantes.forEach(p -> { System.out.println(p); });
Java.
Direto ao código. Dada uma List<Palestrante>
Apesar de trazer muitas novidades, um lambda
palestrantes, como imprimir todos eles?
não pode ser criado de qualquer maneira. O código
palestrantes.forEach(new Block<Palestrante>() {
abaixo não compila:
});
public void apply(Palestrante p) {
System.out.println(p);
}
Quem conhece um pouco da API de coleções sabe
que não há um método forEach dentro de List, nem
em suas principais superinterfaces: Collection e Iterable. Para esse código funcionar, além dessa interface Block precisar existir, esse método forEach precisa
Object o = p -> { System.out.println(p); };
Uma expressão lambda precisa sempre ser atribuída/inferida para uma interface/class abstrata que
possui apenas um único método abstrato (antigamente chamadas de SAM: single abstract method).
A especificação, ainda em andamento, as chamam
agora de functional interfaces. Por exemplo, para um
Runnable, conseguimos inferir um lambda que não
41 \
recebe argumentos:
Runnable r = () -> { System.out.println(); };
Iterable<Integer> posts =
palestrantes.map(Palestrante::getPostsNoGuj);
Dado que temos os inteiros, podemos somá-los
Como o compilador sabe que esse conteúdo vai
para dentro do método run()? Pois é o único método com o reduce (similar ao foldLeft ou inject em outras
abstrato da interface. Assim como o método apply(T linguagens):
o) é o único da interface Block<T>.
Integer total = posts.reduce(0, (total, proximo)
Podemos ir mais adiante. Quando o lambda vai
-> total+proximo);
realizar apenas uma única invocação de método com
O reduce recebe o valor inicial da conta, para detodos os parâmetros recebidos, podemos criá-lo atrapois aplicar, item a item, o lambda dado. Esse lambda
vés de uma referência para um método:
possui dois parâmetros e será inferido a uma implepalestrantes.forEach(System.out::println);
mentação da nova interface BinaryOperator.
E para filtrar quem tem mais de 500 posts no
Assustador? O compilador vai inferir que o Block
GUJ? Passamos um lambda para ser utilizado como
a ser passado para o forEach vai ser uma invocação
implementação do método test da nova interface
do método println em cima de System.out, passando
Predicate:
o único parâmetro que esse Block pode receber: um
palestrante. Se você tivesse passado uma referência
palestrantes.filter(p -> p.getPostsNoGuj() > 500);
para um método que recebe mais de um parâmetro, o
compilador não conseguiria inferir o lambda!
Se você quer invocar o método favoritar em cada
Vale lembrar que isso não é um “method lite- um desses palestrantes, basta utilizar o forEach que
ral”, da mesma forma que temos class literals (como já conhecemos:
String.class, por exemplo). Fazer Method m = System. palestrantes.filter(p -> p.getPostsNoGuj() > 500).
out::println, ou algo semelhante, não compila. Uma
forEach(Palestrante::favoritar);
referência a um método nada mais é que outra forma de escrever um lambda, então possui as mesmas
Quer finalmente ordená-los? Finalmente há o
restrições: há necessidade de uma interface funcional método sort na interface list!
envolvida na expressão.
palestrantes.sort((p1, p2) -> …. )
Assim como o forEach, há muitos novos extensions methods na interface Iterable, conhecidos dos
Passando seu Comparator como argumento, poprogramadores funcionais: map, reduce, filter, grou- dendo ser inferido por um lambda.
pBy, mapped etc. Para pegar o número de posts que
Com esses novos recursos, o código Java se aprocada Palestrante tem no GUJ, fazemos:
xima ao de linguagens como Scala e Ruby. O mesmo
código em Java, Scala e Ruby, sem abusar muito de
Iterable<Integer> posts = palestrantes.map(
outros recursos da linguagem (como o underscore do
p -> p.getPostsNoGuj());
Scala), ficaria:
Nesse caso nem precisamos usar as chaves depois
palestrantes.map(Palestrante::getPostsNoGuj).
dos parâmetros. Não precisamos utilizá-los quando
reduce(0, (n, x) -> n+x);
o conteúdo do método é uma instrução de return. O palestrantes.map(_.postsNoGuj).fold(0)(n,x => n+x)
mesmo ocorre com os parênteses dos parâmetros, palestrantes.map(&:postsNoGuj).inject(0){|n,x| n+x}
que são opcionais no caso de ser apenas um. Em outras palavras, poderíamos escrever:
Esse foi apenas um sucinto começo do que está
por
vir. Vale conhecer todos os métodos dentro de
Iterable<Integer> posts = palestrantes.map((p)
Iterable,
assim como as interfaces do java.util.func-> { return p.getPostsNoGuj(); } );
tions e as novas superinterfaces das collections. Há
Ou ainda podemos usar a notação de method re- ainda references para construtores, novos métodos
ference:
em classes importantes do Java 8 e pequenas melhorias por toda API. Será um grande passo para a pla-
/ mais projetos: Apache Collections
> Existe também o já conhecido projeto Apache Collections, que possui algumas novas interfaces e classes utilitárias para manipular
coleções.
/ 42
/eu uso
Geraldo Ferraz | [email protected]
Analista desenvolvedor da Cast e ministra cursos na Caelum Brasília.
No projeto em que trabalho, chegamos no ponto em que melhorar legibilidade e expressividade tornou-se uma prioridade. Por muito tempo o projeto permaneceu com muitos loops e estruturas de condições encadeadas, e por mais que extraíssemos métodos não conseguíamos ler o código e saber de primeira o que estava sendo feito. Com o LambdaJ conseguimos melhorar todos os aspectos citados, entretanto,
adicionamos bastante complexidade e alta dependência da API. Com alguma refatoração é possível deixar o código de forma que a complexidade fique encapsulada tornando o uso muito mais intuitivo.
/referências
para saber mais/
> “Herança e Composição – os princípios por trás dos
Para baixar os builds do JDK8:
padrões”, ed. 39 da MundoJ – Eduardo Guerra
> http://openjdk.java.net/projects/jdk8/
> “JPA 2: Os novos recursos inspirados no Hibernate”, ed.
> http://openjdk.java.net/projects/lambda/
39 da MundoJ – Paulo Silveira e Raphael Lacerda
> “Enums desmistificadas”, ed. 26 da MundoJ – Alexandre
Gazola
> Série “Design patterns para um Mundo Real”, eds. 21, 22
e 23 da MundoJ – Rodrigo Yoshima
Para ver a evolução do lambda no JDK8, conheça sua
antiga sintaxe e propostas:
> http://blog.caelum.com.br/trabalhando-com-closuresno-java-8/
> http://www.javac.info/
Para conhecer mais do LambdaJ:
> http://blog.caelum.com.br/codigo-expressivo-eprogramacao-funcional-em-java-com-lambdaj
taforma. Ao mesmo tempo, alguns outros itens ainda ficaram de fora, como a sintaxe simplificada para
criação de coleções e literais para membros da classe.
Considerações finais
Utilizar o Guava e o LambdaJ pode trazer diversos
ganhos a sua aplicação. Mas será que o código é mais
expressivo? Possui mais abstração? É mais conciso?
São critérios de difícil definição e há bastante discussão a respeito. Deixamos links na referência sobre
esse assunto.
Mesmo que seu código esteja agora mais expressivo, será que essas novas bibliotecas não serão uma
barreira grande para novos programadores da equipe? É certamente um ponto que deve ser considerado
ao adotar APIs como essas. Assim como o impacto de
performance que o LambdaJ pode trazer por causa do
uso de muitas dynamic proxies, esses são trade-offs
que devem ser estudados.
E quando começar a utilizar o Java 8? Apesar de
já haver milestones sólidos, a API está mudando bastante, além de que não há IDE nenhuma com suporte
a nova sintaxe. É provável que, no início do ano de
2013, tanto Eclipse quanto Netbeans ofereçam suporte.
> http://code.google.com/p/lambdaj/wiki/LambdajFeatures
DSLs, interfaces fluentes e outros patterns envolvidos aqui:
> http://martinfowler.com/dsl.html
> http://martinfowler.com/bliki/FluentInterface.html
> http://blog.jayway.com/2012/02/07/builder-patternwith-a-twist/
Conhecendo mais do Guava:
> http://code.google.com/p/guava-libraries/
Expressividade, abstração e concisão:
> http://www.joelonsoftware.com/items/2006/08/01.html
> http://gafter.blogspot.com.br/2007/03/on-expressivepower-of-programming.html
Um pouco de teoria sobre o cálculo lambda e programação
funcional:
> http://steve.vinoski.net/pdf/IC-Welcome_to_the_
Functional_Web.pdf
> http://blog.caelum.com.br/comecando-com-o-calculolambda-e-a-programacao-funcional-de-verdade/
43 \
Download