Você está na página 1de 7

22/08/2019 O mínimo que você deve saber de Java 8 - Blog da Caelum: desenvolvimento, web, mobile, UX e Scrum

O mínimo que você deve saber


de Java 8
Postado dia 07/03/2019 por Paulo Silveira e Rodrigo Turini em Inovação, Programação 83

Sem dúvida as mudanças do Java 8 foram as mais profundas na plataforma depois de muito tempo.
Aproveitamos para atualizar esse post para também linkar as mudanças do Java 9 e as do Java
10 que, mesmo menores, também são interessantes. Na Caelum trabalhamos por bastante tempo
com as versões beta do Java, por isso conseguimos manter o nosso curso de Java e orientação a
objetos atualizado, com uma seção de classe anônimas e lambdas, além de seu uso em outras APIs,
como as Collections e Streams que veremos aqui. Também escrevemos um livro de Java 8 e a
na nossa carreira Java Júnior da plataforma Alura. São muitas as novidades e sumarizamos aqui as
principais modificações na linguagem e na API. Vamos direto a um código para enxergar os três
principais novos conceitos da linguagem.

Ordenando coleções

Dada uma lista de Strings:

List<String> palavras = Arrays.asList("rodrigo", "paulo", "caelum");

Queremos ordená-la de acordo com o tamanho de cada String. Para isso criamos
um Comparator através de uma classe anônima, como já estamos habituados:

Comparator<String> comparador = new Comparator<>() {

https://blog.caelum.com.br/o-minimo-que-voce-deve-saber-de-java-8/ 1/7
22/08/2019 O mínimo que você deve saber de Java 8 - Blog da Caelum: desenvolvimento, web, mobile, UX e Scrum

public int compare(String s1, String s2) {


return Integer.compare(s1.length(), s2.length());
}
}

E em seguida utilizamos o Collections.sort para ordenar a lista com o critério de comparação


definido:

Collections.sort(palavras, comparador);

A primeira novidade do Java 8 é que podemos fazer essa chamada desta forma:

palavras.sort(comparador);

Sim, há um novo método em java.util.List, que é o sort. Por que não fizeram antes? Pois
adicionar métodos novos em uma interface pode quebrar muito código já existente em quem
implementa List. E o que tem de diferente no Java 8?

Conhecendo os default methods

Abrindo o código da interface List podemos ver como é o sort:

default void sort(Comparator<? super E> c) {


Collections.sort(this, c);
}

Podemos ter métodos concretos em interfaces a partir do Java 8! Basta utilizar o


modificador default. Eles serão ‘herdados’ por todos que implementarem essa interface. Esse
recurso, chamado default method, permite evoluir uma interface sem quebrar compatibilidade. É uma
técnica já bem conhecida no C#, Scala e outras linguagens. Há muitos outros métodos default que
foram adicionados a API de coleções, como Collection.removeIf, Map.getOrDefault e até
mesmo no Comparator, como o Comparator.reversed, que devolve um novo comparador que
ordena ao contrário.

https://blog.caelum.com.br/o-minimo-que-voce-deve-saber-de-java-8/ 2/7
22/08/2019 O mínimo que você deve saber de Java 8 - Blog da Caelum: desenvolvimento, web, mobile, UX e Scrum

Expressão Lambda

Além de ficar mais prático de escrever o código sem o uso direto da Collections, podemos também
criar o Comparator de maneira bem mais enxuta sem utilizar a sintaxe de classe anônima:

Comparator<String> comparador = (s1, s2) -> {


return Integer.compare(s1.length(), s2.length());
};

Essa é a sintaxe do Lambda no Java 8. Ela pode ser utilizada com qualquer interface funcional. Uma
interface funcional é aquela que possui apenas um método abstrato (semanticamente falando pode
haver diferenças). Dessa forma o compilador consegue inferir qual método está sendo implementado
nessas linhas. Diferente da geração de classes em tempo de compilação, como é feito para as
classes anônimas, o lambda do Java 8 utiliza MethodHandles e o invokedynamic.

Reduzindo mais ainda o código

O código pode ficar ainda mais enxuto. Como há apenas uma instrução dentro desse lambda, não
precisamos nem do return, nem do uso das chaves:

Comparator<String> comparador = (s1, s2) -> Integer.compare(s1.length(), s2.lengt

Ou ainda passar tudo isso diretamente como argumento para o sort:

palavras.sort((s1, s2) -> Integer.compare(s1.length(), s2.length()));

Você pode utizar o lambda em qualquer interface antiga que seja considerada funcional:

new Thread(() -> System.out.println("thread nova rodando")).start();

Como um dos construtores de Thread recebe uma interface funcional, o compilador sabe que deve
tentar converter esse lambda para um Runnable! É sempre necessário o envolvimento de uma
interface funcional. O seguinte código não compila:

https://blog.caelum.com.br/o-minimo-que-voce-deve-saber-de-java-8/ 3/7
22/08/2019 O mínimo que você deve saber de Java 8 - Blog da Caelum: desenvolvimento, web, mobile, UX e Scrum

Object o = () -> System.out.println("thread nova rodando");

Compilaria se tivéssemos declarado como um Runnable. Muitos dos novos métodos inseridos nas
interfaces das collections recebem um argumento que é uma interface funcional, como
o Collection.forEach:

palavras.forEach(s -> System.out.println(s));

O forEach recebe um Consumer<T> como argumento, que é uma das muitas novas interfaces do
pacote java.util.function. Ele tem apenas um método, o accept, que recebe T e não devolve
nada. Como o método recebe apenas um argumento, repare que não foi necessário declarar s entre
parenteses no lambda s -> System.out.println(s).

Ordenando as coleções com a expressão lambda

Podemos fazer a nossa ordenação ficar ainda mais sucinta. Interfaces podem ter métodos default
estáticos, agrupando melhor métodos utilitários. O Comparator possui o factory method comparing.
Ele recebe uma Function que, no nosso caso, recebe uma String e devolve algo que queiramos
usar como critério de comparação. Vamos passar um lambda que, dado a String, devolve
seu length:

palavras.sort(Comparator.comparing(s -> s.length());

Escrevendo a expressão lambda com method reference

É muito comum um lambda simplesmente invocar um único método. Um lambda também pode ser
escrito como forma de method reference. Em vez de s -> s.length()fazemos
simplesmente String::length, ficando implícito que queremos, para a Stringpassada como
argumento, que o length seja invocado:

palavras.sort(Comparator.comparing(String::length);

https://blog.caelum.com.br/o-minimo-que-voce-deve-saber-de-java-8/ 4/7
22/08/2019 O mínimo que você deve saber de Java 8 - Blog da Caelum: desenvolvimento, web, mobile, UX e Scrum

Isso é possível pois as duas linhas a seguir são equivalentes:

Function<String, Integer> function = s -> s.length();


Function<String, Integer> function = String::length;

A segunda é até mais fácil para o compilador inferir, já que algumas vezes pode haver ambiguidade e
a necessidade de tipar os argumentos do lambda, como (String s) -> s.length(). E você poderia
passar essa function como argumento para o Comparator.comparing, mas obviamente é
desnecessário, pois podemos passar diretamente o lambda, como fizemos. Para aplicar um filtro
nessa lista, como por exemplo retornar apenas as palavras com menos de 6 caracteres, gostaríamos
de fazer algo como palavras.filter(...). Porém o método filter não foi adicionado em nossa
API de Collection! Existem várias razões para isso, como não querer misturar métodos sem efeitos
colaterais em coleções mutáveis, além de evitar deixar as coleções muito poluídas com tantas novas
funcionalidades.

Aumentando o poder com Stream

Para fazer essa e outras transformações comuns em nossas coleções, contamos agora com uma
nova API, o Stream. Para criar um Stream com os elementos de nossa lista só precisamos chamar o
método defaut .stream() presente na interface Collection (e em outros lugares!).
Fazemos palavras.stream() para ter um Stream<string>. Essa API traz uma forma mais funcional
de trabalhar com nossas coleções. Ela possui diversos métodos, como o filter, map e reduce, que
recebem uma interface funcional como parâmetro, nos possibilitando tirar proveito dos novos recursos
de lambda e method reference.

Filtrando coleções

Para filtrar as Strings com menos de 6 caracteres em nossa lista podemos fazer:

palavras.stream()
.filter(s -> s.length() < 6)
.forEach(System.out::println);

O método filter recebe a interface funcional Predicate como parâmetro. Essa interface possui
apenas o método test que recebe T e retorna um boolean. Você nem precisava saber disso! Com o
https://blog.caelum.com.br/o-minimo-que-voce-deve-saber-de-java-8/ 5/7
22/08/2019 O mínimo que você deve saber de Java 8 - Blog da Caelum: desenvolvimento, web, mobile, UX e Scrum

lambda, os nomes das interfaces funcionais perdem um pouco a importância e tornam o código mais
simples de ler.

Utilizando o map

Outro método que será muito utiizado em nosso dia a dia é o map, podemos e devemos utilizá-lo
quando precisamos aplicar transformações em nossa lista sem a necessidade de variáveis
intermediárias. Para mapear as palavras pelo seu tamanho (length), podemos fazer:

Stream<Integer> stream = palavras.stream().map(String::length);

Porém recebemos um Stream<Integer> como retorno. Para evitar esse boxing desnecessário dos
tipos primitivos a API de Streams possui implementações equivalentes ao %%Stream%% para
eles: IntStream, LongStream, e DoubleStream. Para utilizar um IntStream neste nosso caso,
podemos substituir a chamada do método mappor mapToInt:

IntStream intStream = palavras.stream().mapToInt(String::length);

Em outros locais é possível evitar o boxing e unboxing. Há em Comparator o


método comparingInt que recebe um IntFunction. Podemos fazer aquele sort do inicio do artigo
com palavras.sort(Comparator.comparingInt(String::length)).

Mais sobre o Stream

As implementações de Stream para os tipos primitivos possuem diversos métodos para auxiliar na
manipulação de seus valores, como é o caso do sum, min, max, count, average, entre diversos outros.
O interessante desse ultimo é que o seu retorno será um Optional, outra importante introdução do
java 8:

Optional<Double> media = palavras.stream()


.mapToInt(String::length)
.average();

System.out.println(media.orElse(0));

https://blog.caelum.com.br/o-minimo-que-voce-deve-saber-de-java-8/ 6/7
22/08/2019 O mínimo que você deve saber de Java 8 - Blog da Caelum: desenvolvimento, web, mobile, UX e Scrum

Note que o OptionalDouble é a implementação de Optional para esse tipo primitivo, essa estratégia
é utilizada em grande parte das novas interfaces! Voltando ao nosso filtro, vimos que ao
fazer palavras.stream().filter(s -> s.length() < 6) isso nos retornou um Stream<string>.
Para que o retorno seja uma List<string> contamos com o método collect(). Esse método
espera receber a interface funcional Collector como parâmetro. Para simplificar nosso trabalho, já
existem diversos factory methods de collectors prontos na classe Collectors, como é o caso
do toList():

List<String> resultado = palavras.stream()


.filter(s -> s.length() < 6)
.collect(Collectors.toList());

Ufa! Esse é um pequeno resumo essencial do que entrou nessa versão. Se você tiver interesse, pode
ver como era a sintaxe proposta para o Java 8 em 2011 nesse antigo post. Das novidades que ficaram
de fora do post, temos o suporte a múltiplas anotações do mesmo tipo, a API de java.time baseada
no Joda Time, melhorias na inferência do generics e do operador diamante além de inúmeros ajustes
nas APIs. Muitas features ficaram de fora, como collection literals e value objects. Você pode ver o
que entrou e ficou de fora. Mas quem sabe isso tudo não aparece junto com a reificação do generics
no Java 9?

https://blog.caelum.com.br/o-minimo-que-voce-deve-saber-de-java-8/ 7/7

Você também pode gostar