Buscar

InfoQBR emag javapresentefuturo

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 53 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 53 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 53 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

2 InfoQ Brasil
Onde foi parar o PermGen do Java?
8 funcionalidades pouco conhecidas do Java 8
Anotações de tipos no Java 8: Ferramentas
e oportunidades
Java 8: Desmistificando Lambdas
Quão funcional é o Java 8?
Java 7 - Características que viabilizam o Java 8
Q&A: Novidades do Java 8
Do Groovy ao Java 8
Nashorn: Combinando o poder do Java 
e JavaScript no JDK 8
Java 9 e além: Brian Goetz e John Rose falam
sobre o futuro do Java
Novos Horizontes do Java
EMAG | JAVA: PRESENTE E FUTURO
05.
11.
15.
22.
29.
33.
36.
38.
42.
49.
51.
Onde foi parar o PermGen do Java? . . . . . . . . . . . . . . . . . . . . 5 
 Adeus PermGen. Olá Metaspace! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 
 Mudança para o Metaspace e suas alocações . . . . . . . . . . . . . . . . 6
 Otimização e Metaspace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
 Questões Atuais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
8 funcionalidades pouco conhecidas do Java 8 . . .11
 1. StampedLock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .11
 2. Adicionadores concorrentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .12
 3. Ordenação Paralela . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .12
 4. Migrando para nova API de data . . . . . . . . . . . . . . . . . . . . . . . . . . .12 
 5. Controle de processos do sistema operacional . . . . . . . . . . .13
 6. Operações numéricas precisas . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13
 7. Geração segura de números aleatórios . . . . . . . . . . . . . . . . . . .13
 8. Referências opcionais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13
Anotações de tipos no Java 8: Ferramentas 
e oportunidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .15
 Sintaxe das anotações de tipo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .16
 Detectando erros com anotações . . . . . . . . . . . . . . . . . . . . . . . . . . . .17
 O framework Checker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .18
 Aumentando a produtividade com anotações de tipo . . . . .19
 A estrada à frente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .20
 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .21
Java 8: Desmistificando Lambdas . . . . . . . . . . . . . . . . . . . . . . .22
 Referência de métodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .24
 Evolução da biblioteca . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .25
 Operações de agregação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .25 
 Fontes de stream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .26
 Operações de finalização de Stream . . . . . . . . . . . . . . . . . . . . . . . . .26
 Inteface Iterable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .26
 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .28
Quão funcional é o Java 8? . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
 O que é uma linguagem de programação funcional?. . . . . 29
 Programação funcional em linguagens não 
 funcionais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 
 Java - um pouco de história . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
 O sistema de tipos original do Java . . . . . . . . . . . . . . . . . . . . . . 30
 Alternativas a tipos nomeados . . . . . . . . . . . . . . . . . . . . . . . . . . 31
 O sistema de tipos do Java 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
 Recursos introduzidos no Java 6 e 7 . . . . . . . . . . . . . . . . . . . . . 31 
 O sistema de tipos do Java 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 
 Quão funcional é o Java 8? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Java 7: Características que viabilizam o Java 8 . . . 33 
 Operador Diamante. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
 Manipulador de Métodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
 invokedynamic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Q&A: Novidades do Java 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Do Groovy ao Java 8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Nashorn: Combinando o poder do Java 
e JavaScript no JDK 8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
 Shell scripts. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 
 Passando dados para o Java e vice-versa. . . . . . . . . . . . . . . . . 45
 Usando classes Java no Nashorn. . . . . . . . . . . . . . . . . . . . . . . . . 45
 Linguagem funcional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
 Dialeto especial do JavaScript no Nashorn . . . . . . . . . . . . . . . 46
 Avatar.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
 Resumindo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Java 9 e além: Brian Goetz e John Rose falam
sobre o futuro do Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Novos Horizontes do Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
 Primeiros anúncios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 
 Outras novidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
 Data final quase definida, com alguns sacrifícios . . . . . . . . . 52
CONTEÚDO DETALHADO
4 InfoQ Brasil
O Java 8 é o release atual do Java desde 2014, mas para alguns ainda é um sonho que o legado não deixa se concretizar. Por outro lado, ambientes empresariais e grupos de desenvolve-
dores estão se modernizando e gradualmente tornando o novo Java 
mainstream. Com o Java 9 planejado para o primeiro semestre de 
2017, a versão 8 da linguagem/plataforma mais popular do mundo é 
a realidade que queremos conhecer e alcançar hoje. Com essa eMag, 
buscamos oferecer uma base para você explorar as muitas novidades 
do Java 8 – de novas APIs a mudanças na linguagem. Também vis-
lumbramos um pouco do futuro, concluindo essa coleção de artigos 
do InfoQ com um apanhadodo que está por vir no Java 9.
O Java 8 contém novas funcionalidades, aprimoramentos e corre-
ções de bugs para maior eficiência no desenvolvimento e na execução; 
traz features voltadas ao aumento de produtividade e facilidade de 
uso, além de melhorias em programação poliglota, segurança e per-
formance. Se as versões 6 e 7 do Java foram alterações consideradas 
leves, o Java 8 está em outro patamar. Mais de 56 novas funciona-
lidades foram adicionadas. Chegaram os lambdas, métodos padrão, 
interfaces funcionais e a API de streams, modificando radicalmente 
a linguagem e portanto todo o ecossistema Java. Há ainda uma nova 
API para datas, novas anotações e aumento de desempenho do engi-
ne para JavaScript.
Neste eMag, o InfoQ leva praticantes numa visita através do Java 
8 e as futuras versões da linguagem, explorando como chegamos até 
aqui e algumas formas que seguiremos adiante com a tecnologia.
APRESENTAÇÃO
5eMag | Java: Presente e Futuro 
ONDE FOI PARAR O PERMGEN DO JAVA?
por Monica Beckwith, traduzido por Rafael Brito
Com a introdução do JDK8, não existe mais o espaço de PermGen. Os metadados que antes eram armazenados no PermGen não desapareceram, 
mas foram movidos para a memória nativa, em uma área conhecida como “Metaspace”. Conheça neste artigo maiores detalhes desta importante 
mudança da plataforma Java.
A Máquina Virtual do Java (JVM) utiliza uma represen-tação interna de suas classes 
contendo uma série de metadados por 
classe, tais como: informações de hie-
rarquia de classes, dados e informa-
ções de métodos (tais como byteco-
des, pilha e tamanho de variáveis), o 
pool contínuo em tempo de execução, 
resoluções de referências simbólicas e 
Vtables.
Anteriormente, quando os class-
loaders customizados não eram tão 
comuns, as classes eram “estáticas” 
em sua maioria, raramente eram des-
carregadas ou coletadas, e consequen-
temente eram marcadas como “per-
manentes”. Além disso, desde que as 
classes fizessem parte da implemen-
tação da JVM, e não fossem criadas 
pela aplicação, elas eram considera-
das como “memória não pertencente 
ao heap”.
Na JVM HotSpot antecessora ao 
JDK8, a representação “permanente” 
ficava em uma área chamada per-
manent generation (“geração perma-
nente”). A geração permanente era 
contígua ao heap do Java e limitada a 
-XX:MaxPermSize, que precisava ser 
configurada por linha de comando 
antes de iniciar a JVM, caso contrá-
rio seria iniciada com o valor padrão 
de 64M (85M para 64bit scaled poin-
ters - ponteiros auto-incrementáveis 
com deslocamentos constantes, que 
facilitam o acesso à estrutura). A co-
leção da geração permanente ficaria 
associada à coleção da antiga gera-
ção, então sempre que alguma delas 
ficassem cheias, ambas as gerações 
(permanentes e antigas) seriam cole-
tadas. Um dos problemas mais óbvios 
que se pode verificar de imediato é a 
dependência do -XX:MaxPermSize. Se 
o tamanho das classes de metadados 
estiver além dos limites do -XX:Max-
PermSize, sua aplicação será execu-
tada com pouco espaço de memória 
e um erro de “Out of Memory” será 
apresentado.
Curiosidade: Antes do JDK7, para 
a JVM HotSpot, as Strings internali-
zadas (interned) também eram manti-
das na geração permanente, também 
conhecida como PermGen, causan-
do muitos problemas e erros de Out 
of Memory. Para mais informações 
acesse a documentação desse proble-
ma.
6 InfoQ Brasil
Com o surgimento do JDK8, Perm-
Gen não existe mais. As informações 
de metadados não desapareceram, só 
que o espaço em que eram mantidos 
não é mais contíguo ao heap. Agora, o 
metadado foi movido para a memória 
nativa, em uma área conhecida como 
“Metaspace”.
A mudança para o Metaspace foi 
necessária, pois era realmente difícil 
de se fazer o ajuste fino (tunning) do 
PermGen. Havia ainda a possibilida-
de dos metadados serem ser movidos 
por qualquer coleta de lixo completa. 
Além disso, era difícil dimensionar 
o PermGen, uma vez que o seu ta-
manho dependia de muitos fatores, 
tais como: o número total de classes, 
tamanho dos pools constantes, tama-
nhos dos métodos etc.
Adicionalmente, cada garbage col-
lector na HotSpot precisava de um có-
digo especializado para lidar com os 
metadados no PermGen. Desacoplar 
os metadados do PermGen não só 
permite o gerenciamento contínuo do 
Metaspace, como também melhorias, 
tais como: simplificação da coleta de 
lixo completa e futura desalocação 
concorrente de metadados de classe.
Mudança para o Metaspace e 
suas alocações
Agora a VM Metaspace (Máquina 
Virtual Metaspace) emprega técnicas 
de gerenciamento de memória para 
gerenciar o Metaspace. Movendo as-
sim o trabalho de diferentes coletores 
de lixo para uma única VM Metas-
pace. Uma questão por trás do Me-
taspace reside no fato de que o ciclo 
de vida das classes e seus metadados 
corresponde ao ciclo de vida dos clas-
sloaders. Isto é, quanto mais tempo o 
classloader estiver ativo, mais o meta-
dado permanecerá ativo no Metaspa-
ce e não poderá ser liberado.
Temos usado o termo “Metaspace” 
de forma muito vaga neste texto. Mais 
formalmente, a área de armazenagem 
por classloader é chamado de “me-
taspace”, e estes metaspaces são co-
letivamente chamados “Metaspace”. 
A reciclagem ou recuperação de me-
taspace por classloader pode ocorrer 
somente depois que o seu classloader 
não estiver mais ativo e foi relatado 
como inativo pelo coletor de lixo. Não 
há realocação ou compactação nestes 
metaspaces, mas os metadados são 
varridos por referências Java.
A VM Metaspace gerencia a alo-
cação de Metaspace empregando um 
alocador de segmentos. O tamanho 
dos segmentos depende do tipo de 
classloader. Há uma lista global de 
segmentos livres. Sempre que um 
classloader precisa de um segmento, 
remove-o da lista global e mantém a 
sua própria lista de segmentos. Quan-
do algum classloader é finalizado, 
seus segmentos são liberados e retor-
nam novamente para a lista global de 
segmentos livres. Esses segmentos 
são posteriormente divididos em blo-
cos e cada bloco contém uma unidade 
de metadados. A alocação de blocos 
dos segmentos é linear (ponteiro de 
colisão). Os segmentos são alocados 
fora dos espaços de memória mape-
ada (mmapped). Há uma lista ligada 
para os tais espaços globais mmapped 
virtuais, e sempre que algum espaço 
virtual é esvaziado, ele é devolvido ao 
sistema operacional.
Adeus PermGen. 
Olá Metaspace!
7eMag | Java: Presente e Futuro 
A figura acima mostra a alocação 
de Metaspace com metasegmentos 
em um espaço virtual mmapped. Os 
Classloaders 1 e 3 representam a re-
flexão ou classloaders anônimos e 
empregam segmentos de tamanhos 
“específicos”. Os Classloaders 2 e 4 
podem ser empregados em segmen-
tos de tamanho pequeno ou médio, 
baseado no número de itens em seus 
carregadores.
Como mencionado anteriormen-
te, uma VM Metaspace gerenciará 
o crescimento do Metaspace, mas 
talvez surgirão cenários em que se 
deseje limitar o crescimento através 
do uso da configuração explícita do 
-XX:MaxMetaspaceSize via linha de 
comando. Por padrão, o -XX:MaxMe-
taspaceSize não possui um limite, 
Otimização e Metaspace
então, tecnicamente, o tamanho do 
Metaspace poderia assumir o espaço 
de swap, e começaria a gerar falhas de 
alocação nativa.
Para uma JVM servidora de 64 bits, 
o valor padrão/inicial do -XX:Metas-
paceSize é de 21MB. Esta é a configu-
ração do limite máximo inicial. Uma 
que vez que esta marca é alcançada, 
uma varredura de lixo completa é 
disparada para descarregar classes 
(quando os seus classloaders não esti-
verem mais ativos), e o limite máximo 
é reiniciado. O novo valor deste limite 
depende da quantidade de Metaspace 
liberado. Se o espaço liberado não for 
suficiente, o limite aumenta; se for li-
berado espaço demais, o limite dimi-
nui. Isto serárepetido diversas vezes 
se o limite inicial for muito baixo, e 
será possível constatar a repetida co-
leta de lixo completa nos logs de co-
leta de lixo. Em tal cenário, recebe-se 
o aviso para configurar o -XX:Metas-
paceSize para um valor mais elevado 
através da linha de comando, para 
evitar as coletas de lixo iniciais.
Após coletas subsequentes, a VM 
Metaspace será automaticamente 
ajustada para o limite mais alto, de 
modo a postergar a coleta de lixo do 
Metaspace.
Há também duas opções: -XX:Min-
MetaspaceFreeRatio e -XX:MaxMetas-
paceFreeRatio, que são análogas aos 
parâmetros do GC FreeRatio e podem 
ser configurados através da linha de 
comando.
Algumas ferramentas foram mo-
dificadas para ajudar a obter mais 
informações sobre o Metaspace, e são 
listadas a seguir:
8 InfoQ Brasil
• jcmd <PID> GC.class_stats: Este é um novo comando de diagnóstico que permite ao usuário final se conectar à 
JVM em execução, e obter um histograma detalhado dos metadados das classes Java.
Nota: Com o JDK8 build 13, você deve iniciar o Java com -XX:+UnlockDiagnosticVMOptions.
• jstat -gc <LVMID>: agora exibe informações do Metaspace, como demonstra o exemplo a seguir:
$ jmap -clstats <PID>
Attaching to process ID 6476, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.5-b02
finding class loader instances ..done.
computing per loader stat ..done.
please wait.. computing liveness.liveness analysis may be inaccurate ...
class_loader classes bytes parent_loader alive? type 
<bootstrap> 655 1222734 null live <internal>
0x000000074004a6c0 0 0 0x000000074004a708 dead java/util/ResourceBundle$RBClassLoader@0x00000007c0053e20
0x000000074004a760 0 0 null dead sun/misc/Launcher$ExtClassLoader@0x00000007c002d248
0x00000007401189c8 1 1471 0x00000007400752f8 dead sun/reflect/DelegatingClassLoader@0x00000007c0009870
0x000000074004a708 116 316053 0x000000074004a760 dead sun/misc/Launcher$AppClassLoader@0x00000007c0038190
0x00000007400752f8 538 773854 0x000000074004a708 dead org/dacapo/harness/DacapoClassLoader@0x00000007c00638b0
total = 6 1310 2314112 N/A alive=1, dead=5 N/A
$jstat-gc <LVMID>
SOC SIC SOU S1U EC EU OC OU MC UM CCSC CCSU YGC YGCT FGC FGCT GCT
1024.0 1024.0 0.0 96.0 6144.0 2456.3 129536.0 2228.3 7296.0 6550.3 896.0 787.0 229 0.211 33 0.347 0.558
Where, MC: Current Metaspace Capacity (KB); UM: Metaspace Utilization (KB)
$ jcmd <PID> help GC.class_stats
9522:
GC.class_stats
Provide statistics about Java class meta data. Requires -XX:+UnlockDiagnosticVMOptions.
Impact: High: Depends on Java heap size and content. 
Syntax : GC.class_stats [options] [<columns>] 
Arguments:
 columns : [optional] Comma-separated list of all the columns to show. If not specified, the following columns are shown: 
InstBytes,KlassBytes,CpAll,annotations,MethodCount,Bytecodes,MethodAll,ROAll,RWAll,Total (STRING, no default value)
Options: (options must be specified using the <key> or <key>=<value> syntax)
 -all : [optional] Show all columns (BOOLEAN, false)
 -csv : [optional] Print in CSV (comma-separated values) format for spreadsheets (BOOLEAN, false)
 -help : [optional] Show meaning of all the columns (BOOLEAN, false)
• jmap -clstats <PID>: exibe as estatísticas de um classloader. (Isto agora assume o lugar do -permstat, que era usado 
para exibir estatísticas do classloader de JVMs anteriores ao JDK8). Segue um exemplo de saída no momento de 
execução do benchmark da DaCapo Avrora:
9eMag | Java: Presente e Futuro 
Um exemplo de saída:
$ jcmd <PID> GC.class_stats 
7140:
Index Super InstBytes KlassBytes annotations CpAll MethodCount Bytecodes MethodAll ROAll RWAll Total ClassName
 1 -1 426416 480 0 0 0 0 0 24 576 600 [C
 2 -1 290136 480 0 0 0 0 0 40 576 616 [Lavro-
ra.arch.legacy.LegacyInstr;
 3 -1 269840 480 0 0 0 0 0 24 576 600 [B
 4 43 137856 648 0 19248 129 4886 25288 16368 30568 46936 java.lang.Class
 5 43 136968 624 0 8760 94 4570 33616 12072 32000 44072 java.lang.String
 6 43 75872 560 0 1296 7 149 1400 880 2680 3560 java.
util.HashMap$Node
 7 836 57408 608 0 720 3 69 1480 528 2488 3016 avrora.sim.util.
MulticastFSMProbe
 8 43 55488 504 0 680 1 31 440 280 1536 1816 avrora.
sim.FiniteStateMachine$State
 9 -1 53712 480 0 0 0 0 0 24 576 600 [Ljava.
lang.Object;
 10 -1 49424 480 0 0 0 0 0 24 576 600 [I
 11 -1 49248 480 0 0 0 0 0 24 576 600 [Lavrora.sim.pla-
tform.ExternalFlash$Page;
 12 -1 24400 480 0 0 0 0 0 32 576 608 [Ljava.util.HashMa-
p$Node;
 13 394 21408 520 0 600 3 33 1216 432 2080 2512 avrora.sim.AtmelIn-
terpreter$IORegBehavior
 14 727 19800 672 0 968 4 71 1240 664 2472 3136 avrora.arch.legacy.
LegacyInstr$MOVW
…<snipped>
…<snipped>
1299 1300 0 608 0 256 1 5 152 104 1024 1128 sun.util.resources.
LocaleNamesBundle
 1300 1098 0 608 0 1744 10 290 1808 1176 3208 4384 sun.util.resources.
OpenListResourceBundle
 1301 1098 0 616 0 2184 12 395 2200 1480 3800 5280 sun.util.resources.
ParallelListResourceBundle
 2244312 794288 2024 2260976 12801 561882 3135144 1906688 4684704 6591392 Total
 34.0% 12.1% 0.0% 34.3% - 8.5% 47.6% 28.9% 71.1% 100.0%
Index Super InstBytes KlassBytes annotations CpAll MethodCount Bytecodes MethodAll ROAll RWAll Total ClassName
Como mencionado anteriormente, a VM Metaspace emprega um alocador de segmentos. Há múltiplos tamanhos de 
segmentos, dependendo do tipo de classloader. Além disso, os itens de classe por si mesmos não possuem um tamanho 
fixo, por isso há chances de que os segmentos livres não sejam do mesmo tamanho que os segmentos necessários para os 
itens de classe. Tudo isso pode gerar uma fragmentação. A VM Metaspace (ainda) não utiliza compactação, consequente-
mente a fragmentação é uma das principais preocupações neste momento.
Questões Atuais
10 InfoQ Brasil
Monica Beckwith é uma engenheira de desempenho 
trabalhando na indústria de hardware por mais de uma 
década. Seu mais atual título é Arquiteta de Desempenho 
na Servergy - uma startup que oferece uma nova classe de 
servidores Cleantech hiper-eficientes ®. Antes da Servergy, 
Monica trabalhou na Oracle / Sun e AMD otimizando a JVM 
para sistemas de classe de servidor. Monica foi eleita uma 
palestrante “estrela do rock” no JavaOne 2013. 
Sobre a Autora
Acompanhe Monica no Twitter: 
@mon_beck.
11eMag | Java: Presente e Futuro 
8 FUNCIONALIDADES POUCO 
CONHECIDAS DO JAVA 8
por Tal Weiss, traduzido por Vitor Puente
Este artigo visa apresentar algumas das novas APIs do Java 8 que não estão sendo tão comentadas, mas que tornaram o Java melhor de várias 
maneiras.
O código multithreaded tem sido um desafio para os 
desenvolvedores. Com o tempo, algumas construções 
complexas foram adicionadas às bibliotecas Java visando 
auxiliar o desenvolvedor com códigos multithreaded, e 
seu acesso a recursos compartilhados. Um exemplo é o 
ReadWriteLock, que permite ao desenvolvedor dividir o 
código em seções que precisam ser mutuamente exclusi-
vas (“writers”) e seções que não precisam (“readers”).Na teoria parece ótimo. Porém um problema com Re-
adWriteLock é que a aplicação pode ficar muito lenta, às 
vezes até dez vezes mais lenta. O Java 8 introduziu um 
novo bloqueio ReadWrite, chamado StampedLock. A boa 
notícia é que o StampedLock é muito rápido. A má notícia 
é que é mais complicado de usar e envolve mais estados. O 
StampedLock não é “reentrante”, o que significa que uma 
1. StampedLock thread pode causar um deadlock envolvendo ela mesma.
O StampedLock possui o modo “otimista”, que emite 
uma marcação (stamp) retornada por cada operação de 
bloqueio (lock). Essa marcação é utilizada como uma es-
pécie de ingresso. Cada operação de desbloqueio (unlo-
ck) precisa gerar sua marcação correspondente. Qualquer 
thread que adquirir o bloqueio de escrita, enquanto outra 
thread está com o bloqueio de escrita do modo otimista, 
fará com que o unlock seja invalidado (o stamp não será 
mais válido). Nesse ponto, a aplicação pode começar tudo 
novamente, possivelmente com um lock no modo “pessi-
mista”, que também é implementado pelo StampedLock. 
O gerenciamento de tudo isso fica por conta do desenvol-
vedor. E como um stamp não pode ser utilizado para des-
bloquear outro, deve-se tomar cuidado.
Veja a seguir um exemplo deste tipo de lock em ação:
12 InfoQ Brasil
Outra novidade do Java 8 lida especificamente 
com código escalável: os “Adicionadores Concorren-
tes (Concurrent Adders)”. Um dos padrões mais bási-
cos de concorrência é a leitura e escrita de contadores 
numéricos. Há diversas maneiras de fazer isso hoje 
em dia, mas nenhuma tão eficiente ou elegante como 
a existente no Java 8.
Antes do Java 8, isso era feito utilizando “Atomics”, 
que utilizavam instruções diretas da CPU de compa-
ração e swap (CAS), através da classe sun.misc.Un-
safe, para realizar a tentativa e marcar o valor de um 
contador. O problema era que quando um CAS falha-
va devido a uma contenção, o AtomicInteger ficava 
em loop, tentando continuamente o CAS em um loop 
infinito, até o seu sucesso. Em altos níveis de conten-
ção isso poderia se mostrar muito lento.
A partir do Java 8 foi adicionado o LongAdders. 
Esse conjunto de classes fornece uma maneira con-
veniente de leitura e escrita de valores numéricos 
em cenários de grande escalabilidade. A utilização é 
simples. Basta iniciar um novo LongAdder e utilizar 
os seus métodos add() e intValue() para aumentar e 
retornar o contador.
A diferença entre esta versão do Atomics e a an-
tiga é que, quando o CAS falha devido a alguma 
contenção, em vez de fazer o giro na CPU, o Adder 
armazenará o delta em uma célula interna alocada 
para aquela thread. Então adicionará o valor junto de 
qualquer outro valor de células pendentes ao resul-
tado do método intValue(). Isso reduz a necessidade 
de voltar e realizar o CAS novamente, ou bloquear 
outras threads.
A utilização do modelo de Adicionadores concor-
rentes deve ser sempre preferido frente ao modelo 
Atômico para o gerenciamento de contadores.
2. Adicionadores concorrentes 3. Ordenação Paralela
4. Migrando para nova API de data
Assim como os Adicionadores aceleram os conta-
dores, o Java 8 traz uma maneira concisa de acelerar a 
ordenação. E de uma maneira muito simples, em vez 
de realizar a ordenação desta maneira:
 Array.sort(myArray);
Agora pode utilizar o método pronto para a para-
lelização:
 Arrays.parallelSort(myArray);
Isso automaticamente dividirá a sua coleção em 
partes, as quais serão ordenadas através de um nú-
mero de “cores” e então agrupadas novamente. Há 
uma ressalva, quando o método de ordenação para-
lela é chamado em ambientes com multi-thread ele-
vado, tal como um web container sobrecarregado, os 
benefícios iniciais são reduzidos, até mais de 90%, de-
vido ao aumento de trocas de contexto da CPU.
O Java 8 introduz uma nova API de datas. Uma 
nova maneira de lidar com horas e a maioria dos mé-
todos da versão atual estão marcados como obsoletos. 
A nova API traz a facilidade de uso e a precisão for-
necidas pela popular API Joda Time para o núcleo da 
biblioteca Java.
Assim como em qualquer nova API, as boas novas 
são o estilo mais elegante e funcional. Infelizmente, 
muito já foi feito com a antiga API e a transição deve-
rá levar tempo.
Para ajudar na transição, a classe Date possui um 
novo método chamado “toInstant()”, que converte 
uma data para a nova representação. Isso é especial-
mente útil ao fazer uso de APIs que recebe o formato 
clássico de data, pois basta converter a data para o 
novo formato e utilizar todos os benefícios da nova 
API de datas.
long stamp = lock.tryOptimisticRead(); //sem caminho bloqueante - muito rápido.
work (); // é esperado que não ocorra nenhuma escrita por hora.
if (lock.validate(stamp)){
 //sucesso! Sem contenção com a thread de escrita.
} else {
 //outra thread precisa obter um bloqueio de escrita entretanto mudando o stamp.
 //voltando a ter um bloqueio de leitura mais pesado.
 stamp = lock.readLock(); //Este é uma operação de leitura que causa o lock.
 try {
 //Sem escrita acontecendo agora.
 work();
 } finally {
 lock.unlock(stamp); // Liberação do stamp utilizado.
 }
}
13eMag | Java: Presente e Futuro 
Lançar um processo do sistema operacional di-
retamente do código Java já é possível através das 
chamadas Java Native Interface (JNI), mas com isso é 
bem provável se deparar com resultados inesperados 
ou exceções sérias mais para a frente. Ainda assim, é 
um mal necessário. 
Além disso, os processos têm mais um lado desa-
gradável – o fato de tenderem a ficar suspensos. O 
problema ao iniciar um processo a partir do código 
Java até a versão 7 tem sido difícil o controle sobre um 
processo uma vez iniciado.
Para ajudar nessa tarefa o Java 8 traz três novos 
métodos na classe “Process”:
1. destroyForcibly() termina um processo com 
um grau de sucesso bem maior do que antes;
2. isAlive() informa se um processo iniciado ainda 
está vivo;
3. Uma nova sobrecarga de waitFor() especifica a 
quantidade de tempo para que o processo termine. 
Isso retorna ou o sucesso da execução ou o time-out, 
neste último caso é possível terminar o processo.
Duas boas utilizações para estes métodos são:
• Caso um processo não termine no tempo deter-
minado, forçar seu término e seguir em diante:
if (process.wait(MY_TIMEOUT, TimeUnit.MILLISECONDS)){
 //success! 
} else {
 process.destroyForcibly();
}
• Garantir que, antes que um código termine, não 
seja deixado nenhum processo para trás. Proces-
sos suspensos certamente esgotarão seu sistema 
operacional, mesmo que lentamente.
for (Process p : processes) {
 if (p.isAlive()) {
 p.destroyForcibly();
 }
}
5. Controle de processos do sistema 
operacional
7. Geração segura de números aleatórios
8. Referências opcionais
6. Operações numéricas precisas
Um overflow numérico pode causar alguns dos 
mais estranhos problemas em software, dada sua 
característica implícita. Isso é especialmente verda-
de em sistemas que possuem contadores inteiros in-
crementados de acordo com o tempo. Nesses casos, 
recursos que funcionam corretamente em pré-produ-
ção, podem começar a se comportarem de maneira 
completamente estranha de repente, quando as ope-
rações sofrem o overflow e produzem valores ines-
perados.
Para auxiliar nesse problema, o Java 8 adicionou 
diversos métodos “exact” à classe Math, protegendo 
um código suscetível a overflows, através do lança-
mento de “uncheckedArithmeticException” quando 
o valor de uma operação causa um overflow.
int safeC = Math.multiplyExact(bigA, bigB); 
// Será lançada uma ArithmeticException caso o 
resultado exceda +-2 3^1
O único aspecto negativo disso é que a responsa-
bilidade em identificar como o código pode ter pro-
blemas com overflow fica porconta de quem está de-
senvolvendo. Não há solução mágica, porém isso já é 
melhor que nada.
O Java tem sido criticado por anos pelos proble-
mas de segurança. Justo ou não, um grande esforço 
tem sido feito para blindar a JVM e os frameworks de 
possíveis ataques. Os números aleatórios com baixo 
nível de entropia tornam sistemas que os utilizem na 
criação de chaves para criptografia ou códigos hash 
mais suscetíveis a ataques.
Até o momento, a escolha do algoritmo de geração 
de números aleatórios é de responsabilidade do de-
senvolvedor. O problema é que, nas implementações 
que dependem de um hardware, sistema operacional 
ou JVM específicos, o algoritmo desejado pode não 
estar disponível. Em tais casos, as aplicações têm ten-
dência a migrar para versões mais fracas de gerado-
res, o que aumenta o risco de ataques.
O Java 8 adicionou o novo método SecureRandom.
getInstancStrong() com o objetivo de permitir que a 
JVM escolha uma versão segura. Se escrever código 
sem o completo controle sobre o hardware/SO/JVM 
em que será executado, deve-se considerar o uso des-
te método. (É uma situação comum quando se cons-
troem aplicações a serem executadas na nuvem ou 
em ambientes de PaaS.)
As exceções do tipo NullPointerException são bem 
antigas. Porém ainda hoje estão presentes no dia-a-
-dia de uma equipe de desenvolvimento, não importa 
quão experiente a equipe seja. Para ajudar com este 
14 InfoQ Brasil
problema o Java 8 introduz um novo template cha-
mado Optional<T>.
Baseado em linguagens como Scala e Haskell, esse 
template indica explicitamente quando uma referên-
cia passada ou retornada por uma função pode ser 
nula. Isso ajuda a reduzir as dúvidas se uma referên-
cia pode ou não ser nula, devido à confiança em do-
cumentações que podem estar desatualizadas, ou a 
códigos que podem mudam com o tempo.
Optional<User> tryFindUser(int userID) {
ou
void processUser(User user, Optional<Cart> 
shoppingCart) {
O template Optional possui funções que tornam 
o trabalho mais conveniente, como “isPresent()” que 
verifica se um valor não-nulo está disponível, ou 
“ifPresent()”, que recebe uma função Lambda que 
será executada caso “isPresent()” seja verdadeiro. O 
lado negativo é, assim como ocorre com a nova API 
de Data e Hora, levará tempo até que está novidade 
esteja presente nas diversas APIs usadas diariamente.
Aqui está a nova sintaxe Lambda para escrever um 
valor opcional:
value.ifPresent(System.out::print);
Tal Weiss é o CEO da Takipi. Weiss vem desenhando aplicações escalá-
veis, de tempo real em Java e C++ nos últimos 15 anos. Ele ainda gosta de 
analisar um bom bug através da instrumentação de código Java. Em seu 
tempo livre Weiss toca Jazz na bateria.
Sobre o Autor
15eMag | Java: Presente e Futuro 
ANOTAÇÕES DE TIPOS NO JAVA 8: FERRAMENTAS 
E OPORTUNIDADES
por Todd Schiller, traduzido por Wellington Pinheiro
As anotações no Java 8 podem ser escritas não apenas em declarações, mas também em qualquer uso de tipos como nas declarações, generics e 
conversões de tipos (cast). Nesse artigo são apresentadas as anotações de tipos e as ferramentas que ajudam a construir aplicações melhores.
Nas versões anteriores do Java era possível anotar somente declarações. Com o Java 8 as anotações podem ser escritas em qualquer local que os ti-
pos são usados, como por exemplo: declarações, generics 
e conversões de tipos (casts), como apresentado no código 
a seguir:
@Encrypted String data;
List<@NonNull String> strings;
myGraph = (@Immutable Graph) tmpGraph;
À primeira vista, as anotações de tipo não aparentam 
ser uma das funcionalidades mais atraentes dessa nova 
versão do Java. Ao contrário, em relação à linguagem em 
si, as anotações possuem somente uma sintaxe e as fer-
ramentas é que dão a sua semântica, isto é, significado e 
comportamento.
Como desenvolvedor Java, é provável que as anotações 
já estejam sendo utilizadas para melhorar a qualidade do 
software. Considere a anotação @Override, introduzida 
no Java 1.5. Em projetos com muitas heranças não triviais, 
torna-se difícil rastrear qual implementação de um méto-
do será executado. Nesse contexto, se não forem tomados 
os devidos cuidados ao modificar a assinatura de um mé-
todo, isso pode fazer com que o método de uma subclasse 
deixe de ser executado, pois ele deixará de sobrescrever 
o método da superclasse recém alterado. Eliminar a cha-
mada de um método dessa forma pode introduzir uma 
falha ou alguma vulnerabilidade. Em decorrência disso, 
a anotação @Override foi introduzida. Ela permite que 
os desenvolvedores deixem explícito os métodos que so-
brescrevem outros métodos da superclasse. O compilador 
Java usa essa informação para advertir o desenvolvedor 
quando o código não reflete essa intenção. Usando essa 
abordagem, as anotações agem como um mecanismo 
para auxiliar na verificação automática do programa.
Além da verificação automática, as anotações têm de-
sempenhado um papel central para aumentar a produti-
vidade através de técnicas de metaprogramação. A ideia 
é que as anotações podem informar as ferramentas sobre 
16 InfoQ Brasil
como gerar código auxiliar, fazer transformações no 
código ou definir como o programa deverá se com-
portar em tempo de execução. Por exemplo a API de 
persistência JPA (Java Persistence API), introduzida 
no Java 1.5, permite que os desenvolvedores especifi-
quem de forma declarativa uma correspondência en-
tre objetos Java e entidades do banco de dados através 
de anotações, tal como: @Entity. Ferramentas como o 
Hibernate podem usar essas anotações para fazer ma-
peamentos e consultas SQL em tempo de execução.
No caso do JPA e do Hibernate, as anotações são 
usadas para evitar a escrita de código repetitivo, re-
duzindo assim a duplicidade de código - princípio co-
nhecido como Não Se Repita (Don’t Repeat Yourself 
- DRY). Curiosamente, sempre que se olha para as fer-
ramentas que auxiliam na aplicação de boas práticas, 
não é difícil encontrar o uso de anotações. Alguns 
exemplos notáveis ajudam na redução do acoplamen-
to através da Injeção de Dependência e também na 
separação de responsabilidades com a Programação 
Orientada a Aspectos
Mas se as anotações já estão sendo usadas para 
melhorar a qualidade do código, por que usar anota-
ções de tipos?
Uma resposta simples é que as anotações de ti-
pos trazem mais possibilidades. Elas permitem, por 
exemplo, que mais tipos de falhas sejam detectados 
automaticamente e dão mais controle às ferramentas 
de produtividade.
As anotações de tipo no Java 8 podem ser escritas 
em qualquer local em que um tipo é usado, como por 
exemplo:
@Encrypted String data
List<@NonNull String> strings
MyGraph = (@Immutable Graph) tmpGraph;
Criar uma anotação de tipo é muito simples, 
basta usar o ElementType.TYPE_PARAMETER, 
ElementType.TYPE_USE ou ambos, na definição do 
alvo (target) da anotação.
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
public @interface Encrypted { }
O ElementType.TYPE_PARAMETER indica que 
a anotação pode ser usada na declaração de tipos, tais 
como: classes ou métodos com generics ou ainda jun-
to de caracteres coringa em declarações de tipos anô-
nimos. O ElementType.TYPE_USE indica que a ano-
Sintaxe das anotações de tipo
tação pode ser aplicada em qualquer ponto que um 
tipo é usado, por exemplo em declarações, generics e 
conversões de tipos (casts). O código a seguir exem-
plifica alguns dos usos desses tipos de anotações:
@TypeUse class MyClass<@TypeParameter T extends @TypeUse 
Integer> {
 @TypeUse <@TypeParameter S> MyClass(String s) {
 ... 
 }
 public static void main(String ... args) {
 Object o = ( @TypeUse String ) new String( “Test” );
 if( o instanceof @TypeUse String ){
 ...
 }}
}
Assim como as anotações em declarações, as 
anotações de tipos podem ser salvas junto com 
o arquivo de bytecode gerado e acessadas em 
tempo de execução via reflection através das 
políticas de retenção: RetentionPolicy.CLASS ou 
RetentionPolicy.RUNTIME, usadas na definição 
da anotação. Existem duas diferenças fundamentais 
entre as anotações de tipo e as suas predecessoras. 
Primeiro, as anotações de tipo aplicadas a variáveis 
locais podem ser salvas junto com o bytecode. 
Segundo, o tipo usado com generics também é salvo 
e fica acessível em tempo de execução.
Embora as anotações fiquem disponíveis em tem-
po de execução, elas não afetam a execução do pro-
grama. Por exemplo, um desenvolvedor pode de-
clarar duas variáveis do tipo File e uma variável do 
tipo Connection no corpo de um método, da seguinte 
forma:
File file = new File(“aFile”);
@Encrypted File encryptedFile = new File(“anEncryptedFile”);
@Open Connection connection = getConnection();
Supondo que a classe Connection tenha um mé-
todo send(File), é possível executar esse método pas-
sando como parâmetro qualquer uma das duas vari-
áveis do tipo File:
connection.send(file);
connection.send(encryptedFile);
Como era de se esperar, a ausência de efeito em 
tempo de execução implica que, embora os tipos pos-
sam ser anotados, os métodos não podem ser sobre-
carregados com base nas anotações dos tipos.
public class Connection{
17eMag | Java: Presente e Futuro 
 void send(@Encrypted File file) { ... }
 // A tentativa de sobrecarga a seguir resulta em um erro 
de compilação
 // void send( File file) { ... }
 . . .
}
A intuição por trás dessa limitação é que o com-
pilador não tem conhecimento sobre a relação entre 
tipos anotados e não anotados ou entre tipos com di-
ferentes anotações.
Repare que há uma anotação @Encrypted na va-
riável encryptedFile coincidindo com o parâmetro 
file na assinatura do método send, mas perceba que 
não há nada nesse método que corresponda à anota-
ção @Open na variável connection. Quando é feita a 
chamada connection.send(...), a variável connection é 
conhecida como uma receptora (receiver) do método 
(o termo receiver é uma analogia clássica à troca de 
mensagens entre objetos na teoria de Orientação a 
Objetos). O Java 8 introduz uma nova sintaxe para as 
declarações de métodos de forma que anotações de 
tipos possam ser escritas em um receptor no método. 
void send(@Open Connection this, @Encrypted File file)
Observe nesse último exemplo a sintaxe para defi-
nir o método send. O primeiro parâmetro na verdade 
faz referência á instância receptora, por isso o nome 
desse parâmetro é “this”.
Como citado anteriormente, as anotações não afe-
tam a execução do programa. Assim, um método 
declarado com a nova sintaxe do parâmetro receptor 
tem o mesmo comportamento àquele utilizando a 
sintaxe tradicional. Na prática, o uso da nova sintaxe 
só permite que anotações de tipos sejam escritas no 
tipo do receptor.
Uma explicação completa da sintaxe de anotações 
de tipos, incluindo sintaxe para arrays multi-dimen-
sionais, pode ser encontrada na especificação JSR 
(Java Specification Request) 308.
Detectando erros com anotações
Escrever anotações no código serve para enfatizar 
erros quando o código tem algum problema:
@Closed Connection connection = ...;
File file = ...;
…
connection.send(file); // Problema!: fechada e não 
criptografada!
Contudo, esse último código de exemplo pode ser 
A anotação @MaybeTainted pode ser vista como 
um supertipo da anotação @Untainted. Existem vá-
rias maneiras de pensar nessa relação. Primeiro, o 
conjunto de valores que podem ter sido adulterados 
podem ser um superconjunto de valores que sabida-
mente não foram adulterados (um valor que não foi 
adulterado pode ser um elemento desse superconjun-
to). Dessa forma, a anotação @Untainted fornece uma 
garantia maior que @MaybeTainted. Na prática, essa 
compilado, executado e causará erro durante a execu-
ção. O compilador Java não faz verificações de anota-
ções definidas pelo usuário. Apesar disso, a platafor-
ma Java expõe duas APIs que auxiliam nessa tarefa, 
uma para a criação de plugins para o Compilador e 
outra para o processamento de anotações, de forma 
que terceiros possam construir seus próprios méto-
dos de análise.
Nos exemplos anteriores, as anotações tinham a 
função de qualificar os valores que as variáveis po-
deriam conter. Porém, podem ser pensadas outras 
formas de qualificar o tipo File: @Open, @Localized, 
@NonNull, etc; também pode ser pensada na aplica-
ção dessas anotações qualificando outros tipos, como 
por exemplo: @Encrypted String. Devido ao fato das 
anotações serem independentes do sistema de tipos 
do Java, os conceitos expressos através delas podem 
ser reutilizadas de muitas formas.
Mas como essas anotações poderiam ser automa-
ticamente verificadas? Intuitivamente, algumas ano-
tações são subtipos de outras anotações e suas apli-
cações podem ser verificadas em relação aos tipos. 
Considere a vulnerabilidade ao ataque de Injeção de 
SQL, causado pela execução de sentenças SQL modi-
ficadas maliciosamente pelo usuário. Pode-se pensar 
em um tipo de dados classificado como @MaybeTain-
ted, indicando que o dado pode ter sido adulterado, 
ou @Untainted, indicando que o dado está garantida-
mente livre de adulteração, pois não foi diretamente 
informado pelo usuário.
@MaybeTainted String userInput;
@Untainted String dbQuery;
18 InfoQ Brasil
O framework Checker
tipagem poderia funcionar da seguinte forma:
userInput = dbQuery; // OK
dbQuery = “SELECT FROM * WHERE “ + userInput; // erro de 
tipo!
Nesse exemplo, a primeira linha não contém ne-
nhum problema, pois userInput supõe que o valor 
atribuído pode ter sido adulterado, mesmo que esse 
não seja o caso. Por outro lado, essa tipagem revela 
um erro na segunda linha, pois está atribuindo um 
valor que pode ter sido adulterado a uma variável 
que contém essa restrição.
O Checker é um framework que faz verificações de 
anotações em Java. Lançado em 2007, esse framework 
é um projeto de código aberto encabeçado pelo sub-li-
der da especificação JSR 308, professor Michael Ernst. 
O Checker vem com um conjunto padrão de anota-
ções e verificadores de falhas, tais como: NullPointe-
rException, incompatibilidade no uso de unidades de 
medidas, vulnerabilidades de segurança, problemas 
de concorrência, entre outros. Por ser um verificador 
que utiliza mecanismos formais de verificação de ti-
pagem baseado em lógica, ele é capaz de reconhecer 
falhas potenciais que verificadores baseados em heu-
rísticas não detectam. O framework usa uma API de 
compilador que detecta as falhas durante a compila-
ção. Outra característica é que ele é estensível, o pró-
prio desenvolvedor pode criar rapidamente os seus 
verificadores de anotações para detectar problemas 
específicos da aplicação em questão.
A meta do Checker é detectar falhas sem que o 
desenvolvedor tenha que escrever muitas anotações. 
Isso é feito principalmente através de duas funciona-
lidades apelidadas como: padrões mais inteligente e 
controle de fluxo sensíveis. Por exemplo, durante o 
processo de verificação de falhas por ponteiros nu-
los, o verificador assume que os parâmetros de um 
método não são nulos por padrão. O verificador pode 
também utilizar condicionais para determinar quan-
do uma atribuição de nulo é uma expressão válida.
void nullSafe(Object nonNullByDefault, @Nullable Object 
mightBeNull){
 nonNullByDefault.hashCode(); // OK, devido ao padrão
 mightBeNull.hashCode(); // Falha!
 if (mightBeNull != null){
 mightBeBull.hashCode(); // OK, devido ao padrão
 }
}
Na prática, os padrões e o controle de fluxo sensí-
veis significam que o desenvolvedor raramente teráque escrever anotações no corpo dos métodos, pois o 
verificador é capaz de inferir e verificar as anotações 
automaticamente. Mantendo a semântica das anota-
ções fora do compilador oficial do Java garante que 
ferramentas de terceiros e os próprios desenvolvedo-
res tomem as suas decisões. Isso permite que a veri-
ficação de erros possa ser personalizada conforme as 
necessidades de cada projeto. 
A habilidade de definir suas próprias anotações 
também permite que seja considerada a verificação 
de subtipos específicos do domínio de negócio. Por 
exemplo, em um sistema financeiro, as taxas de ju-
ros usam frequentemente porcentagens enquanto a 
diferença entre taxas utiliza ponto base (1/100 de 1%). 
Com um framework de verificação de unidades po-
dem ser definidas as anotações @Percent e @Basis-
Point para garantir que não há confusão no uso dos 
dois tipos:
BigDecimal pct = returnsPct(...); // anotado para devolver @Percent
requiresBps(pct); // erro: requer @BasisPoints
No exemplo apresentado, devido ao fato do Che-
cker ser sensível ao fluxo de controle, sabe-se que 
pct é um @Percent BigDecimal quando é feita a cha-
mada para requiresBps(pct) com base em dois fatos: 
returnsPct(...) é anotado para devolver um @Percent 
BigDecimal e pct não foi novamente atribuído antes 
de chamar requireBps(...). Frequentemente, desenvol-
vedores usam convenções de nomes para tentar pre-
venir esses tipos de falha. O que o Checker faz é dar 
ao desenvolvedor a garantia de que essas falhas não 
existam, mesmo que o código mude e evolua.
O Checker já foi executado em milhões de linhas 
de código, apontando centenas de falhas, mesmo em 
código bem testado. Um dos exemplos mais interes-
sante aconteceu ao executá-lo com a biblioteca muito 
utilizada Google Collections, agora Google Guava, 
que revelou um possível NullPointerException que 
não tinha sido detectado após vários testes e nem 
após utilizar ferramentas heurísticas de análise está-
tica de falhas.
Resultados assim foram obtidos sem fazer muitas 
alterações no código existente. Na prática, verificar 
uma propriedade com o Checker requer somente 2 
ou 3 anotações para uma centena de linhas de código.
Para aqueles que utilizam o Java 6 ou 7, também é 
possível usar o Checker para melhorar a qualidade 
do código. As anotações podem ser escritas em co-
mentários, tal como: 
19eMag | Java: Presente e Futuro 
/* @NonNull */ String aString
Historicamente, a razão disso é que o Checker foi 
desenvolvido junto com a JSR 308, que teve início em 
2006.
As anotações são especificações declarativas para 
informar como as ferramentas devem gerar código 
ou arquivos auxiliares e como as ferramentas devem 
se comportar durante a execução do programa. O uso 
das anotações desse modo pode ser considerado uma 
forma de metaprogramação. Alguns frameworks, tal 
como o Lombok, tiram vantagem da metaprograma-
ção com anotações ao extremo, resultando em um có-
digo que mal parece Java.
Considere a Programação Orientada a Aspectos 
(Aspect Oriented Programming - AOP). A AOP aju-
da na separação de propriedades ortogonais de uma 
aplicação, tais como log e autenticação, daquelas rela-
cionadas às regras de negócio. Com a AOP é possível 
Apesar do Checker ser o melhor framework para 
fazer verificação de falhas utilizando anotações, ele 
não é o único atualmente. Tanto o Eclipse quanto o 
IntelliJ dão suporte a esse tipo de anotação:
executar um aplicativo em tempo de compilação que 
adiciona código ao programa com base em um con-
junto de regras. Por exemplo, pode ser definida uma 
regra que automaticamente faz autenticação com 
base no tipo anotado:
void showSecrets(@Authenticated User user){
 // Inserido automaticamente pela AOP:
 if (!AuthHelper.EnsureAuth(user)) throw . . .;
}
Como antes, a anotação está qualificando o tipo. 
Ao invés de verificar as anotações em tempo de com-
pilação, a AOP é usada para fazer a verificação em 
tempo de execução automaticamente. Esse último 
POSSUEM SUPORTE 
Checker Framework Suporte completo, incluindo anota-
ções em comentários.
Eclipse Suporte à verificação de valores não 
nulos.
IntelliJ IDEA Podem ser escritos inspecionadores 
personalizados, suporte à verificação 
de valores não nulos.
NÃO POSSUEM SUPORTE 
PMD 
Coverity 
Check Style Sem suporte para Java 8.
Find Bugs Sem suporte para Java 8.
Aumentando a produtividade com anotações de tipo
A ideia inicial por trás das anotações de tipo era a verificação de falhas. Contudo, ainda haviam muitas apli-
cações convincentes desse tipo de anotação em ferramentas de produtividade. Para entender um pouco melhor 
o porquê, considere os seguintes exemplos de como as anotações são usadas
Programação Orientada a Aspectos @Aspect, @Pointcut, etc.
Injeção de Dependência @Autowired, @Inject, etc.
Persistência @Entity, @Id, etc.
20 InfoQ Brasil
exemplo mostra a anotação de tipo sendo usada para 
dar um controle maior sobre como e quando a AOP 
modifica o programa.
O Java 8 também dá suporte às anotações de tipo 
em declarações locais que são persistidas nos arqui-
vos de bytecode. Isso cria novas oportunidades para 
se ter uma AOP mais granular. Por exemplo, para 
incluir código de rastreamento de uma forma mais 
granular:
// Faz o rastreamento de todas as chamadas ao objeto ar
@Trace AuthorizationRequest ar = . . .;
Novamente, as anotações de tipo dão maior con-
trole ao se fazer metraprogramação com AOP. A In-
jeção de Dependência é uma história similar. Com o 
Spring 4, é finalmente possível usar generics como 
qualificador:
@Autowired private Store<Product> s1;
@Autowired private Store<Service> s2;
Com generics é possível eliminar a necessidade 
introduzir classes como ProductStore e ServiceStore 
ou usar regras mais frágeis de injeção baseada em 
nomes.
Com anotações de tipo, não é difícil imaginar que 
um dia o Spring permita usá-las para controlar a inje-
ção da seguinte forma:
@Autowired private Store<@Grocery Product> s3;
Esse exemplo mostra um tipo anotado servindo 
como um mecanismo de separação de interesses, fa-
vorecendo para que a hierarquia de tipos do projeto 
seja mais concisa. Essa separação só é possível por-
que as anotações de tipo são independentes do siste-
ma de tipagem do Java.
Um bom exemplo dessa abordagem é o framewok 
EnerJ, de Adrian Sampson, para computação com 
eficiência energética utilizando a computação aproxi-
mada. EnerJ baseia-se na observação de que em certas 
ocasiões, tal como o processamento de imagens em 
dispositivos móveis, faz sentido reduzir a precisão 
para poupar energia. Um desenvolvedor usando o 
EnerJ pode anotar um dado que não é crítico usan-
do a anotação @Approx. Com base nessa anotação, o 
ambiente de execução do EnerJ usa vários atalhos ao 
manipular esse dado. Por exemplo, pode ser utiliza-
do um hardware de cálculos aproximados de baixo 
consumo de energia para armazenar e fazer cálculos. 
Contudo, tendo dados aproximados se movendo pelo 
programa pode ser perigoso, e nenhum desenvolve-
dor vai querer controlar o fluxo afetado por esse dado 
aproximado. Portanto, o EnerJ usa o Checker para ga-
rantir que os dados aproximados não sejam utiliza-
dos em sentenças de controle de fluxo, por exemplo, 
em sentenças condicionais como ifs.
Mas as aplicações dessa abordagem não são limi-
tadas a dispositivos móveis. Em finanças, frequen-
temente há um conflito entre precisão e velocidade. 
Nesses casos, o ambiente de execução pode decidir 
entre usar um algoritmo de Monte Carlo para o cálcu-
lo de caminhos, um critério de convergência ou mes-
mo processar algo em um hardware especializado 
com base nas demandas atuais ou na disponibilidade 
de recursos.
A beleza desse abordagem é que a preocupação de 
como uma execução deve ser feita fica fora da lógica 
de negócio central da aplicação,que descreve o que 
deve ser feito.
Como apresentado, as anotações de tipo podem 
ser usadas tanto para detectar como prevenir erros 
em programas e também para aumentar a produti-
vidade. Contudo, o potencial real das anotações de 
tipos está em combinar a verificação de erros e a me-
taprogramação para dar suporte aos novos paradig-
mas de desenvolvimento.
A ideia básica é construir ambientes de execução 
e bibliotecas que alavanquem a abordagem de criar 
programas automaticamente mais eficientes, parale-
los ou seguros e que façam com que os desenvolvedo-
res usem as anotações corretamente.
A estrada à frente
21eMag | Java: Presente e Futuro 
Todd Schiller é diretor da FinLingua, uma companhia de consultoria 
e desenvolvimento de software para a área financeira. A prática de 
consultoria da FinLingua ajuda equipes de desenvolvimento a adotar 
uma linguagem específica de domínio, metraprogramação e técnicas 
de análise de programas. Todd é um membro ativo da comunidade de 
pesquisa em engenharia de software; sua pesquisa em especificação e 
verificação tem sido apresentada nas principais conferências interna-
cionais, incluindo ICSE e OOPSLA.
Sobre o Autor
No Java 8, as anotações podem ser usadas em qualquer tipo, incrementando a capacidade de escrever anota-
ções em declarações. As anotações por sí próprias não afetam o comportamento do programa. Contudo, utili-
zando ferramentas como o Checker, é possível utilizá-las para verificar automaticamente a ausência de falhas e 
aumentar a produtividade com metaprogramação. Enquanto ainda levará algum tempo para que as ferramentas 
existentes obtenham total vantagem das anotações de tipos, agora é hora de começar a explorar como as anota-
ções de tipos podem melhorar tanto a qualidade do software desenvolvido quanto a produtividade.
Conclusão
22 InfoQ Brasil
JAVA 8: DESMISTIFICANDO LAMBDAS 
por Simon Ritter, traduzido por Rafael Sakurai
O artigo destaca os pontos principais da palestra “Lambdas & Streams” que Simon Ritter apresentou no QCon London 2014. Ele explica o que são as 
expressões Lambdas, interface funcional, referência de métodos, Streams, operações de agregação e diversos exemplos
Se perguntarmos aos desenvolvedores Java sobre o Java 8, teremos diversas respostas bem animadas, especialmente sobre o uso das expressões lambda.
Mas após uma conversa mais honesta, encontramos 
um fervor misturado com um pouco de receio em relação 
às novas e misteriosas APIs disponíveis na Web. Simon 
Ritter revelou alguns dos mistérios na apresentação sobre 
lambdas na conferência QCon London 2014.
A seguir, temos um trecho de código Java que ilustra 
um padrão comum:
Nota: As partes em vermelho são aquelas que estamos 
interessados; e as partes em azul representam código re-
petitivo.
Neste problema, queremos encontrar a maior nota em 
uma coleção de estudantes. Usamos um idioma comum 
de iteração externa para percorrer e comparar cada ele-
mento da coleção.
Mas há algumas desvantagens no código. Em primeiro 
lugar, a iteração externa significa que os desenvolvedores 
são responsáveis pela implementação (programação im-
perativa), e como é usado um único loop, definimos que 
a execução do código será de modo sequencial. Se qui-
sermos otimizá-lo, não poderíamos facilmente segmentar 
em um conjunto de execução de instruções em paralelo.
Em segundo lugar, a variável highestScore é mutável 
e não é thread-safe. Então, mesmo que quiséssemos que-
brá-lo em múltiplas threads, precisaríamos adicionar um 
lock (bloqueio) para prevenir race conditions (condições 
de concorrência), que por sua vez podem introduzir pro-
blemas de desempenho.
Agora, se quisermos agir de modo mais inteligente, 
podemos mudar um pouco mais a implementação em 
direção ao estilo funcional utilizando uma classe interna 
anônima:
23eMag | Java: Presente e Futuro 
Nessa implementação, eliminamos o estado mu-
tável e passamos o trabalho da interação para a bi-
blioteca. Estamos encadeando uma sequência de cha-
madas de métodos para aplicar a operação em uma 
expressão “Olhe para todos os meus estudantes e fil-
tre apenas aqueles que se graduaram em 2011”.
A classe interna anônima implementa a interface 
Predicate (ela contém um método, aceita um parâme-
tro e retorna um valor booleano) com o método cha-
Pense nas expressões lambda como um método, 
no sentido de que ele aceita parâmetros, tem corpo e 
retorna um tipo estático.
Nesse exemplo, usamos as expressões lambda 
para obter o mesmo algoritmo que determina qual a 
maior nota, como nos exemplos anteriores. Vejamos 
em detalhes.
Primeiro, criamos um Stream a partir de uma Col-
lection. O método stream é novo na interface Collec-
tion e funciona de forma parecida com um Iterator 
embutido (veremos em mais detalhes logo adiante). 
O stream prepara os resultados da coleção, que en-
tão passa para o método filter, descrevendo “como” 
as partes serão filtradas usando a expressão lambda 
que compara o ano de graduação dos estudantes com 
mado “op”, que simplesmente compara o ano de gra-
duação do estudante com 2011 e retorna o resultado.
Enviaremos o resultado (todos os estudantes gra-
duados em 2011) para o método “map”, que usará ou-
tra classe interna anônima para chamar um método 
da interface de mapeamento com seu único método 
“extract” para extrair o dado que queremos (chaman-
do o método getScore). Então passaremos esse resul-
tado, que é um conjunto de notas de todos estudantes 
graduados em 2011, para o método “max”, que enfim 
entregará o maior valor a partir do conjunto de re-
sultados.
Usando essa abordagem, tratamos toda a intera-
ção, filtro e acúmulo com o uso da biblioteca, sem 
precisar fazer isso de forma explícita. Isso não somen-
te simplifica a implementação, como também elimina 
o compartilhamento de estado, deixando mais fácil 
pedir ao código da biblioteca para decompor a imple-
mentação em diversas sub tarefas e alocá-lo em di-
ferentes threads para serem executadas em paralelo. 
Em muitos casos, também executamos uma avaliação 
posterior, economizando ainda mais tempo.
Então, uma abordagem que usa classe interna 
anônima é rápida e thread-safe, mas atente-se para 
as cores do código fonte. Note que a quantidade em 
azul é maior do que em vermelho, indicando código 
repetitivo.
Então, entram em cena as expressões lambda!
2011.
Note que não há um retorno explicito. Simples-
mente dizemos “Compare o ano de graduação com 
2011” e o compilador deduz que destina-se a interface 
Predicate (que tem um único método que necessita de 
uma assinatura com um retorno do tipo booleano). 
O método map é processado de forma similar, usan-
do uma expressão lambda, passando um estudante S 
como parâmetro e mapeando (como um tradutor) seu 
valor de retorno, que é a nota (score) do estudante. O 
método map não deve ser confundido com o java.util.
Map que usa pares de chave-valor. O método map da 
classe Stream retorna uma nova instância do Stream 
contendo os resultados da operação aplicada para to-
dos os elementos da Stream de entrada, produzindo, 
24 InfoQ Brasil
nesse caso, um Stream com todas as notas.
Usando lambdas implementamos o mesmo algo-
ritmo com muito menos código. É mais compreensí-
vel, portanto menos propenso a erros, e como visto, 
ele pode ser alterado para um algoritmo paralelo uma 
vez que não há compartilhamento de estado.
Como Ritter disse em sua apresentação:
Ritter ampliou esse conceito apontando que uma 
vez que uma lambda é uma função sem uma classe, 
então a palavra-chave “this” não se refere a própria 
lambda, mas sim a classe na qual foi declarada. Isso 
distingue de uma classe interna anônima, na qual 
“this” refere-se a própria classe interna.
É útil olhar para as decisões de implementações 
que os designers da linguagem fizeram para desen-
volver os lambdas.Olhando para o Java como um todo, há muitas in-
terfaces que possuem apenas um método.
Vamos definir uma interface funcional como uma 
interface com exatamente um método abstrato, por 
exemplo:
interface Comparator<T> { boolean compare(T x, T y); } 
interface FileFilter { boolean accept(File x); } 
interface Runnable { void run(); } 
interface ActionListener { void actionPerformed(…); } 
interface Callable<T> { T call(); } 
Uma expressão Lambda permite definir uma inter-
face funcional (novamente, um método abstrato) que 
A sintaxe da referência de método é outra nova 
funcionalidade da expressão lambda. É um atalho 
que permite reutilizar um método basicamente como 
uma expressão Lambda. Podemos fazer coisas como:
 
FileFilter x = f -> f.canRead();
Essa sintaxe diz para o programa criar um FileFil-
ter que filtra os arquivos com base em uma proprie-
dade comum – nesse caso, se o arquivo pode ser lido. 
Note que nesse exemplo, nunca mencionamos que f é 
um arquivo; o compilador infere através da assinatu-
ra do único método no FileFilter:
boolean accept(File pathname);
Podendo ser simplificado ainda mais usando a 
nova notação do Java 8 “::”.
FileFilter x = File::canRead;
o compilador identifica pela estrutura. O compilador 
pode determinar a interface funcional representada a 
partir de sua posição. O tipo de uma expressão lamb-
da é o da interface funcional associada. 
Como o compilador conhece os locais que são usa-
dos uma expressão lambda, é possível determinar 
muito sobre essa expressão. Portanto como o compi-
lador sabe o tipo da interface funcional, então pode 
inferir os outros tipos necessários.
“As expressões Lambda representam uma função 
anônima. Então, elas são como métodos, mas não são 
realmente um método. Elas são como funções anô-
nimas no sentido de que elas tem as mesmas funcio-
nalidades de um m étodo, mas elas não são métodos 
porque não estão associadas com uma classe. Se pen-
sar no Java como programamos hoje, criamos classes 
e classes têm métodos. Portanto o método tem uma 
classe associada a ele. No caso das expressões Lambd 
dentro de uma expressão Lambda e ela tem um corpo, 
que defini o que ela fará. Também é possível fazer as 
mesmas coisas que um método: pode agrupar instru-
ções; pode usar chaves e ter múltiplas instruções sem 
nenhum problema. A coisa mais importante sobre isso 
é que agora permite usar uma maneira simples de ter 
um procedimento parametrizado, não apenas valores 
parametrizados.”
Mas, Ritter avisa:
“Um ponto de atenção é que embora não tenhamos 
explicitamente colocado o tipo da informação lá, isso 
não significa que está usando uma tipagem dinâmica 
no Java. Nunca faríamos isso, é desagradável e ruim. 
Então, o que fazemos é dizer que isso ainda é uma tipa-
gem muito estática, mas com menos digitação.”
Ainda segundo Ritter, uma coisa que diferencia as 
expressões lambda dos closures, é que ao contrário 
dos closures, as lambdas não podem acessar as vari-
áveis que estão fora da expressão lambda, exceto as 
variáveis que são efetivamente final, isso significa que 
embora a variável não precisa da palavra-chave final 
(ao contrário de uma classe interna), no entanto o seu 
valor não pode ser reatribuído.”
Referência de métodos
25eMag | Java: Presente e Futuro 
Essas sintaxes são completamente equivalentes.
Para chamar um construtor de forma análoga, 
pode ser utilizado a sintaxe “::new”. Por exemplo, se 
tivermos uma interface funcional como:
interface Factory<T> { 
 T make(); 
} 
Então, podemos dizer:
 
 Factory<List<String>> f = ArrayList<String>::new; 
Isso é equivalente a:
Factory<List<String>> f = () -> return new ArrayList<String>();
 
E agora, quando f.make() é chamada, será retorna-
do um novo ArrayList<String>.
Usando as interfaces funcionais, o compilador 
pode deduzir muitas coisas sobre a tipagem e inten-
ção, como demonstrado nesses exemplos.
Operações de negócios frequentemente envolvem 
agregações como: encontrar a soma, máximo, ou mé-
dia de um conjunto de dados, ou grupo de alguma 
coisa. Até agora, tais operações são normalmente exe-
cutadas com loops de interação externa, como disse-
mos, nos restringindo das otimizações e adicionando 
boilerplate ao código fonte.
As Streams do Java SE 8 tem como objetivo resol-
ver estes problemas. Nas palavras de Ritter:
Uma das vantagens das lambdas e expressão de 
códigos como dados é que, como visto, as bibliotecas 
existentes foram atualizadas para aceitar lambdas 
como parâmetros. Isso introduz alguma complexida-
de: como introduzir métodos na interface sem que-
brar as implementações das interfaces que já existem?
Para fazer isso, o Java introduz o conceito de mé-
todos de extensão, também conhecido como defender 
métodos ou métodos default (padrão).
Vamos explicar usando um exemplo. O método 
stream foi adicionado na interface Collection para 
fornecer um suporte básico ao lambda. Para adicio-
nar o método stream na interface sem quebrar as im-
plementações existentes da Collection de todo mun-
do, o Java adicionou o stream como um método da 
interface, fornecendo uma implementação padrão:
interface Collection<E> { 
 default Stream<E> stream() { 
 return StreamSupport.stream(spliterator()); 
 } 
}
Então, agora temos a opção de implementar o mé-
todo stream ou se preferir usar a implementação pa-
drão fornecida pelo Java.
A resposta é que potencialmente não será finaliza-
do. É possível fazer facilmente um trecho de código 
usando streams que continua para sempre, como se 
fosse um loop “while(true);” infinito. Ele é como um 
Stream: se usar um Stream infinito, ele pode nunca 
terminar. Mas também é possível fazer um Stream 
parar - digamos, para fornecer um Stream infinito de 
números aleatórios, mas com um ponto de parada. 
Assim o Stream vai parar e o programa pode conti-
nuar sua execução.
O Stream fornece um pipeline (sequência) de da-
dos com três componentes importantes:
1. Uma fonte de dados;
2. Zero ou mais operações intermediarias, forne-
cendo um pipeline de Streams;
3. Uma operação terminal(final) que pode realizar 
duas funções: criar um resultado ou um efeito co-
lateral. (Um efeito colateral significa que talvez não 
consiga retornar um resultado, mas ao invés disso, 
consiga imprimir o valor.)
Evolução da biblioteca
Operações de agregação
“Um stream é a maneira de abstrair e especificar 
como processar uma agregação. Ele não é uma estru-
tura de dados. Na realidade é uma maneira de tratar os 
dados, mas define uma nova estrutura de dados, e in-
teressantemente pode tanto finito quanto infinito. En-
tão, é possível criar um stream de, digamos, números 
aleatórios e não precisa mais ter um limite. É aqui que, 
algumas vezes, as coisas ficam um pouco confusas. Re-
flita sobre a seguinte questão: - Se tenho um stream 
infinito, posso continuar processando os dados para 
sempre. Como faço para parar o que estou fazendo 
com os dados?”
26 InfoQ Brasil
Nesse exemplo, iniciamos com uma Collection de 
“transações” e queremos determinar o preço total de 
todas as transações que foram realizadas por com-
pradores de Londres. Obtemos um stream a partir da 
Collection de transações.
Depois obtemos um stream a partir da Collection 
de transações. Então, aplicamos uma operação de fil-
tro para produzir um novo Stream com os comprado-
res de Londres.
A seguir, aplicamos a operação intermediaria 
mapToInt para extrair os preços. E finalmente, aplica-
mos a operação final de soma para obter o resultado.
Do ponto de vista da execução, o que aconteceu 
aqui é que o filtro e o método de map (a operação 
intermediaria) não realizaram um trabalho compu-
tacional. Foram apenas responsáveis por configurar 
o conjuntodas operações, e a computação real é exe-
cutada posteriormente, postergado até chamar a ope-
ração final – nesse caso a soma (sum) – que faz todo 
trabalho acontecer.
Depois de encadearmos todos esses streams, po-
demos especificar uma operação de finalização para 
executar as pipeline e todas as operações (sequencial-
mente ou em paralelo) e produzir os resultados finais 
(ou efeitos colaterais).
int sum = transactions.stream().
 filter(t -> t.getBuyer().getCity().equals(“London”)). //Lazy
 mapToInt(Transaction::getPrice). //Lazy
 sum(); //Executa o pipeline
Essa é uma velha conhecida dos dias do Java 1.5, ex-
ceto que agora tem um método forEach() que aceita 
um Consumer, que aceita um simples argumento e 
não retorna valor e produz um efeito colateral. Mas 
continua sendo uma interação externa e a melhor for-
ma de obter um lambda é o método map().
Exemplos:
Ritter conclui a apresentação com alguns exemplos 
uteis, que estão listados a seguir com comentários ex-
plicativos. (As linhas em negrito indicam o especial 
uso demonstrado em cada exemplo).
Há diversas formas de obter um Stream. Muitos 
métodos estão sendo adicionados a API de Collection 
(usando a extensão de métodos nas interfaces).
Através de uma List, Set, ou Map.Entry é possível 
chamar um método de Stream que retorna uma Stre-
am com o conteúdo da coleção.
Um exemplo é o método stream(), ou parallelStre-
am(), que internamente a biblioteca usa o framework 
de fork/join para decompor as ações em diversas sub-
tarefas.
Há outras maneiras de obter um stream:
• Fornecer um array para o método stream() da 
classe Arrays;
• Chamar o método Stream.of(), um método está-
tico da classe Stream;
• Chamar um dos novos métodos estáticos para 
retornar um stream em particular, por exemplo:
 » IntStream.range(), fornecendo um índice de 
inicio e fim. Por exemplo, IntStream.range(1, 10) 
gera um stream de 1 a 9 com incremento de 1 
em 1 (IntRange.rangeClosed(1, 10) gera um stre-
am de 1 a 10);
 » Files.walk() passando um caminho e algum 
parâmetro de controle opcional que retorna um 
stream de arquivos individuais ou subdiretó-
rios;
 » Implementar a interface java.util.Spliterator 
para definir sua própria maneira de criar um 
Stream. Para mais informações sobre o Splite-
rator consulte o Javadoc do SE 8 fornecido pela 
Oracle.
Fontes de stream
Operações de finalização de Stream
Inteface Iterable
27eMag | Java: Presente e Futuro 
Exemplo 1. Converter palavras para maiúsculo:
List<String> output = wordList.stream().
// Mapa de toda a lista de String em maiúsculo.
map(String::toUpperCase).
// Converte o stream para uma lista.
collect(Collectors.toList());
Exemplo 2. Procurar as palavras 
com tamanho par na lista:
List<String> output = wordList. 
stream(). 
//Seleciona somente as palavras com tamanho par.
filter(w -> (w.length() & 1 == 0). 
collect(Collectors.toList()); 
Exemplo 3. Contar as linhas de um arquivo:
long count = bufferedReader. 
// Recebe um stream com linhas individuais. 
Esse é o novo método do
// bufferedReader que retorna um stream<string>.
lines(). 
// Conta os elementos do stream de entrada.
count(); 
Exemplo 4. Juntar as linhas 3 e 4 em uma única String:
String output = bufferedReader. 
lines(). 
// Pula as duas primeiras linhas.
skip(2).
// limita a stream a apenas as próximas duas linhas.
limit(2).
// Concatena as linhas.
collect(Collectors.joining()); 
Exemplo 5. Encotrar o tamanho da linha mais longa 
em um arquivo:
int longest = reader.lines().
mapToInt(String::length). 
// cria um novo stream com o tamanho das strings mapeando
// a atual String ao tamanho correspondente.
max(). 
// Coleta o maior elemento do stream de tamanho (como 
uma optionalInt)
getAsInt();
// Atualiza o OptionalInt com um int.
Exemplo 6. Coleção de todas as palavras do arquivo
em uma lista:
List<String> output = reader. 
lines(). 
flatMap(line -> Stream.of(line.split(REGEXP))). 
// Recebe um stream de palavras de
// todas as linhas.
filter(word -> word.length() > 0). 
// Filtra as Strings vazias.
collect(Collectors.toList()); 
// Cria a lista de retorno.
Exemplo 7. Retorna a lista de palavras 
minúscula em ordem alfabética:
List<String> output = reader.lines(). 
flatMap(line -> Stream.of(line.split(REGEXP))). 
filter(word -> word.length() > 0). 
map(String::toLowerCase). 
// Atualiza o Stream da fonte com o Stream de
// letras minúsculas.
sorted(). 
// Atualiza o stream com a versão ordenada.
collect(Collectors.toList()); 
// Cria e retorna uma Lista
28 InfoQ Brasil
Simon Ritter é o diretor do Evangelismo da tecnologia Java na 
Oracle Corporation. Ritter trabalha com negócios de TI desde 1984 
e é bacharel de Ciência graduado em Física pela universidade de 
Brunel no Reino Unido.
Sobre o Autor
Simon Ritter conclui a apresentação declarando:
O Java 8 está disponível para download e há um bom suporte a lambda em todos as principais IDEs. Sugiro 
que todos os desenvolvedores Java façam o download e usem o Projeto Lambda. 
Conclusão
“O Java precisa das expressões lambda para facilitar a vida do desenvolvedor. As expressões lambdas eram ne-
cessárias para a criação dos Streams e também para implementar a ideia de passagem de comportamento como a 
passagem de valor. Também precisávamos ampliar as interfaces existentes, com o uso das extensões de métodos do 
Java SE 8, e que resolve o problema da retro compatibilidade. Isso permite fornecer a ideia de operações em lote na 
Collections e permite fazer coisas que são mais simples, e de um modo mais legível. O Java SE 8 está basicamente 
evoluindo a linguagem; evoluindo as bibliotecas de classes e também as maquinas virtuais ao mesmo tempo.”
29eMag | Java: Presente e Futuro 
QUÃO FUNCIONAL É O JAVA 8? 
por Ben Evans, traduzido por Roberto Pepato
Tem sido falado que o Java 8 trouxe a Programação Funcional para o Java. Neste artigo, Ben Evans discute o que significa ser funcional. Olhando 
a evolução do Java — em particular o seu sistema de tipos, é possível ver como os novos recursos do Java 8, especialmente as expressões lambda, 
mudam o panorama e fornecem alguns benefícios fundamentais para o estilo de programação funcional.
Tem-se falado muito sobre como “o Java 8 trouxe a Programação Funcional para o Java” - mas, o 
que isso realmente quer dizer?
Neste artigo, será apresentado o 
que significa ser funcional para uma 
linguagem, ou para um estilo de 
programação. Olhando a evolução 
de Java, em particular o seu sistema 
de tipos (type system), é possível ver 
como os novos recursos do Java 8, 
especialmente as expressões lamb-
da, mudam este cenário e oferecem 
alguns dos principais benefícios da 
programação funcional. 
ritmos como estruturas mais funda-
mentais que os dados que operam. 
Algumas destas linguagens buscam 
desmembrar o estado do programa de 
suas funções (de uma forma que pare-
ce contrária ao desejo das linguagens 
orientadas a objetos, que normalmen-
te buscam manter algoritmos e dados 
integrados).
Um exemplo seria a linguagem de 
programação Clojure. Apesar de exe-
cutar sobre a Java Virtual Machine, 
que é baseada em classes, a Clojure é 
fundamentalmente uma linguagem 
funcional e não expõe diretamente as 
Em sua essência, uma linguagem 
de programação funcional é aquela 
que trata da mesma forma tanto o có-
digo como os dados. Isto significa que 
uma função deve ser um valor de pri-
meira classe na linguagem e deve po-
der ser atribuída a variáveis, passada 
como parâmetro para funções, entre 
outras funcionalidades.
De fato, muitas linguagens funcio-
nais vão ainda mais longe que isso 
e enxergam a computação e os algo-
O que é uma linguagem de 
programação funcional?
30 InfoQ Brasil

Continue navegando