Código de estudo de APIs do Java 8
- Default Methods
- Lambdas
- Method References
- Interface Function
- Streams
- Optional
Com o Java 8, foi adicionado os Default Methods, que permitem que uma Interface adicione um novo método, sendo este concreto. Isso possibilitou que uma Interface possa evoluir sem quebrar compatibilidade.
Para simplificar os códigos, foi introduzido os lambdas.
- forEach com classe anônima:
palavras.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
- forEach com Lambda:
palavras.forEach(s -> System.out.println(s));
Através da dedução feita pelo compilador, é possível simplificar o código. Neste exemplo acima, o método o forEach() recebe um Consumer que só fornece o método accept().
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
(As interfaces que só tem um método abstrato, são chamadas de Funcional Interface.)
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
Sabendo que o forEach() só aceita um Consumer e que este só tem o método accept(), o compilador induz tudo isso, tornando redundante passar uma classe anônima como parâmetro.
Ao invés disso, é passado um lambda, como se fosse a implementação do método accept(), a cada argumento é feito um comando.
O Method References é uma maneira de escrever lambdas com o objetivo de deixar o código mais legível, já que os tipos ficam mais evidentes.
No caso abaixo, o objeto está invocando um método de sua classe.
//lambda
palavras.sort(Comparator.comparing(s -> s.length()));
//lambda com method references
palavras.sort(Comparator.comparing(String::length));
Entretanto, também é possível que este objeto seja passado como argumento de um método de outra classe, como no exemplo abaixo.
//lambda
palavras.forEach(s -> System.out.println(s));
//lambda com method references
palavras.forEach(System.out::println);
Esta interface é uma Funcional Interface, ou seja, só tem um método abstrato (apply). Na sua declaração, ela recebe duas classes, sendo a primeira a Classe que será passada como argumento do método apply e a segunda o será retornado.
//Fuctional Interface
Function<String, Integer> function = new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return s.length();
}
};
Stream é um fluxo de objetos. Não é uma collection, não altera a coleção original.
Através do Stream, temos vários métodos para manipular esse fluxo de objetos.
cursos.stream()
.filter(c -> c.getAlunos() >= 100) //filtra cursos com numero de alunos >= 100
.map(Curso::getAlunos)//faz um map do numero de alunos
.forEach(System.out::println); //imprime o numero de alunos
Também existem Stream para cada tipo primitivo, que fornecem métodos específicos para manipular esses tipos de dados.
int sum = cursos.stream()
.filter(c -> c.getAlunos() >= 100)
.mapToInt(Curso::getAlunos)//retorna um IntStream
.sum();
Se for necessário coletar os dados de um Stream para uma Collection, existe o método collect() que retorna uma collection de acordo com o parâmetro recebido.
cursos = cursos.stream()
.filter(c -> c.getAlunos() >= 100)
.collect(Collectors.toList()); //Retorna uma lista
Map<String, Integer> collect = cursos.stream()
.filter(c -> c.getAlunos() >= 100)
.collect(Collectors.toMap(Curso::getNome, Curso::getAlunos));
Uma nova classe do Java que permite trabalhar com referencias sem precisar criar ifs para checar se o objeto é nulo ou precisar tratar exceções. Além de oferecer diversos métodos que podem tirar proveito dos lambdas.
cursos.stream()
.filter(c -> c.getAlunos() >= 100)
.findAny(). //retorna um Optional
ifPresent(curso -> System.out.println(curso.getNome())); //Realiza uma ação se o objeto for não-nulo.