O mínimo que você deve saber de Java 8
Depois de anos de espera, o Java 8 tem sua versão final disponível para download!
Na Caelum trabalhamos há algum tempo com a versão beta do Java 8. É por esse motivo que nosso curso de Java básico já se encontra atualizado, com uma seção de classe anônimas e lambdas, além de seu uso em outras APIs. Também escrevemos um livro de Java 8 e a na nossa carreira Java Júnior da plataforma Alura.
Esse post escrito em 2014, foi atualizado agora nesse ano de 2017 com novos links, formatação e conteúdo
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<String>() { 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.
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.length());
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:
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
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 String
passada como argumento, que o length
seja invocado:
palavras.sort(Comparator.comparing(String::length);
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
.
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 String
s 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 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 map
por 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:
OptionalDouble media = palavras.stream() .mapToInt(String::length) .average(); System.out.println(media.orElse(0));
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
. Para que o retorno seja uma List
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?
Programação, Mobile, Front-end, Design & UX, Infraestrutura e Business
Rodrigo Turini, gostava de saber se a biblioteca gráfica do Java ainda permanece a Swing que substitui a AWT?
Supplier criadorDeUsuarios = Usuario::new;
Usuario novo = criadorDeUsuarios.get();
Usuario u = new Usuario();
Qual mais simples?
Definitivamente construtores references NAO !
Oi Bruno, Swing permanece sim.
Uma outra alternativa interessante é o JavaFX. []’s
Quanto ao constructor reference, de fato nesse caso usar um new
da forma tradicional é muito melhor! Mas ele não deveria ser usado
assim, um bom exemplo de uso desse recurso é dentro do Collector:
Rodrigo ajuda-me. Quero entender isto mas estou confuso.
O objectivo é ordenar a lista por preço!!!!!
Se esta classe livro não implementa a interface comparable, como faço para dar certo
antes tinha de fazer assim: public class Livro implements Comparable ……
Fiz uma classe assim é não dá.
Package livraria;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author gusto
*/
public class Livro {
private String nome;
private int preco;
public Livro(String nome, int preco){
this.nome = nome;
this.preco = preco;
}
public Livro(){
}
public String getNome(){
return this.nome;
}
public int getPreco(){
return this.preco;
}
public void setPreco(int preco){
this.preco = preco;
}
public void setNome(String nome){
this.nome = nome;
}
public String toString(){
return nome + ” ” + preco;
}
//Este seria o metodo da interace que usaria na main que esta a seguir -> veja
public int comparar(){
Comparable c = (Livro livro) -> {
if(this.preco livro.preco){
return 1;
}
return 0;
};
return 0;
}
}
……………………………………………………………………
package livraria;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
*
* @author gusto
*/
public class Livraria {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
List lista = new ArrayList();
lista.add(new Livro(“primiero”, 100));
lista.add(new Livro(“primiero”, 10));
lista.add(new Livro(“primiero”, 550));
lista.add(new Livro(“primiero”, 110));
lista.add(new Livro(“primiero”, 1000));
lista.add(new Livro(“primiero”, 1));
Collections.sort(lista); …….. da erro aqui, com o código antigamente não dava erro, pois a classe Livro implementava a interface Comparable
}
}
Olá Bruno, se você for usar o Collections.sort(lista) a sua classe Livro precisará
implementar Comparable. O que você pode fazer é usar o novo default method
sort da interface List, assim como mostramos nos primeiros exemplos do post:
Se preferir, você pode mandar suas dúvidas sobre a linguagem no GUJ.com.br. []’s
Rodrigo desculpa de novo o incomodo.
ActionListener, tem interface, certo ? Para botões.
Tava a tentar o KeyListener e tava a dizer que só dava com classes anónimas ? para teclado !!! Enter
como é isso .
Prezados, fiquei com a seguinte dúvida:
Agora que existe a possibilidade de adicionar implementações em metodos de uma interface e no java é possível implementar mais de uma interface, podemos considerar que a caracterisitca de herança multipla direta no java passa a existir?
Oi Carlos, bom ponto! A idéia dos `default methods` é possibilitar a evolução das interfaces sem quebrar as implementações existentes. É comum confundir isso com herança multipla, mas não há, por exemplo, acesso a atributos de instância. Isso não mudou, uma interface não pode ter esse tipo de atributo.
Olá Rodrigo e Paulo. Sou ex-aluno da Caelum inclusive o Rodrigo foi meu instrutor,rs. Gostaria de saber mais sobre os desenvolvedores do Java 8, sobre o Brian Gates, assim que escreve. Não to sabendo achar o caminho para buscar referências deles, estou fazendo a minha monografia sobre o Java 8 e preciso de muitas referências. Obrigado
Oi Eduardo, tudo bem? Legal saber que está escrevendo sobre o assunto.
Alguns links que podem te ajudar são…
a página do Brian Goetz na JCP:
https://www.java.net/jcp/communityspotlight/brian-goetz
JSR 355 (proposta do lambda):
https://www.jcp.org/en/jsr/detail?id=335
página do projeto lambda no openjdk:
http://openjdk.java.net/projects/lambda/
Documento “State of the Lambda”:
http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html
Oi Rodrigo tudo bem? Gostaria de saber as expressões lambdas do Java 8 incorporou as características das closures? Vi numa postagem do devmedia mas isso antes de lançarem oficialmente o Java 8. Como já disse estou escrevendo meu tcc sobre este assunto e não posso escrever besteira. Desde já agradeço pela ajuda.
O Java 8 veio com muitas facilidades, espero que mantenham esse raciocínio para o Java 9.
massa 🙂
#lateonparty
Alguém sabe dizer como esta o JavaFX ou foi abandonado?
Acho ótimo a quantidade de atualizações que estão vindo para o java, mas o que anda me preocupando muito são os tipos inferidos. Os, que logo serão, infames var e val. Java vai ficar parecido com o javascript o que não é um elogio. Quando fizeram isso pensaram em quem está escrevendo o código, mas esquecem que a maior parte do trabalho está em manter o mesmo. Eu iria detestar ter que ficar olhando o valor que uma variável está recebendo para saber o tipo dela. Java é uma linguagem fortemente tipada e ela não pode abrir mão disso.
Ai as pessoas podem dizer “ah…mas se não gosta, é só não usar”. O problema é que tem muito desenvolvedor que quer se mostrar atualizado e que vai usar isso apenas pra mostrar que sabe, fora as empresas que provavelmente adotaram isso apenas para ficarem na “crista da onda”. O restante das coisas são fodásticas, algumas causam estranhamento de início mas nada que não se acostume e não se aprenda a gostar. Não, var e val não da pra se acostumar e muito menos gostar.
Estou começando com o java, e lendo esse artigo vi que na interface java.util.Comparator há dois métodos abstratos: compare() e equals().
Então não entendi duas coisas, porque a interface manda implementar apenas o compare() e porque tendo a anotação FunctionalInterface não é lançada um exceção de compilação?
Oi Jones
Por padrão, todo objeto já herda a implementação do método `equals` da classe `Object`:
https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals(java.lang.Object)
Ele é considerado uma interface funcional porque possui apenas 1 método abstrato, que é o `compare`
Bom dia preciso usar um método que conta o número de palavras em uma String eu utilizei o método split, mas não deu muito certo, gostaria de uma sugestão ou ajuda de como posso implementar sem uso de laço, pois minha tarefa é implementar sem usar laço, apenas com métodos da API Java.lang:
Segue o Código Abaixo
package com.acme.tipos;
import java.util.Arrays;
public class ExemploString {
public static void main(String[] args) {
String s = “Ijuí” ;
int t = s.length();//contador de caracterers da string (lenght)
String a = s.toUpperCase();//coloca a sting em maiuscula
System.out.println(t);
System.out.println(a);
String texto;
texto =”Participaram”;
boolean result1 = texto.contains(“bomba”);//busca na string texto e salva na boolean a palavra em aspas
boolean result2 = texto.contains(“sexo”);//busca na string texto e salva na boolean a palavra em aspas
boolean result3 = texto.contains(“armas”);//busca na string texto e salva na boolean a palavra em aspas
int sa = texto.length();//contador de caracterers da string (lenght)
String[] palavras = texto.split(“”);
String letras = texto.replaceAll(texto, texto);
String ya = texto.replace(‘a’,’A’);//substitui ‘a’ por ‘A’.
String yb = texto.replace(” “, “”);
System.out.println(result1+” para a palavra bomba”);
System.out.println(result2+” para a palavra sexo”);
System.out.println(result3+” para a palavra armas”);
System.out.println(sa+” caracteres”);
System.out.println(palavras.length);
System.out.println(ya);
System.out.println(yb);
System.out.println(“cidade = “+s); //escreve cidade = Ijuí
}
}
Oi Giovane
Você consegue fazer sim com um split simples, sem laço. Algo como:
texto.trim().split(“\\s+”).length
O \\s+ é uma expressão regular pra capturar todos os espaços entre uma palavra e outra.
dica: no futuro, use o GUJ ou fórum da Alura, caso seja aluno lá, pra conseguir esse tipo de ajuda com código. A comunidade lá é bem ativa e receptiva, você vai ter sua dúvida respondida rapidinho (;
Obrigado pela Ajuda Rodrigo,Parabéns pelo seu Trabalho .
Abraço
não consigo usar java nos tribunais de justiça brasileiro, fvr informar como devo proceder
para habilitar no TJRJ E TJMG, já possuo certificado digital