Lambdas e Streams! Influência funcional no Java
MATEUS MALAQUIAS
Baiano | Pedreiro de Software na @ClaroBrasil | Estudando e evangelizando #java, #nodejs, #serverless, #aws | Editor do @trainingcentr 😆
Training Center
Nosso objetivo é inserir pessoas na área de desenvolvimento através de apoio em fóruns de discussão, mentoria, grupos de estudos, code review, exposição de perfis profissionais para as empresas e compartilhamento de conhecimento através de artigos e vídeos.
Sou Java
O SouJava é um grupo de usuários, formado por desenvolvedores e evangelistas da tecnologia Java no Brasil, e tem como objetivo fortalecer, expandir e profissionalizar o uso de Java no país. O SouJava é um dos mais ativos e importantes grupos de usuários do mundo, e realiza diversas atividades no Brasil, e ajuda na organização do movimento Java mundial.
Eita, clica
no coração!
Contextualizando
Precisamos entender
os "por ques"
Usando de um costume antigo
List<Human> humans = Arrays.asList(
new Human("Malaquias"),
new Human("Mateus")
);
Collections.sort(humans, new Comparator<Human>(){
@Override
public int compare(Human h1, Human h2) {
return h1.getName().compareTo(h2.getName());
}
});
Agora usando lambdas
var humans = List.of(
new Human("Schwarzenegger"),
new Human("Malaquias")
);
var humansSorted = humans.stream()
.sorted(Comparator.comparing(Human::getName));
humansSorted .forEach(h -> System.out.println(h.getName()));
Pontos negativos no uso
de classes anônimas
- Sintaxe volumosa;
- Confusão em torno do significado dos nomes;
- Semântica de carga inflexível e criação de instâncias;
- Incapacidade de capturar variáveis locais não finais;
- Incapacidade de abstrair sobre o fluxo de controle da JVM.
Vantagens de usar lambdas
- Vai solucionar os pontos negativos citados;
- Abre novas possibilidades com o estilo funcional;
- O código fica mais enxuto e sucinto;
- Você e a JVM vão usar paralelismo de uma forma mais facíl.
ATENÇÃO Java continua sendo uma linguagem orientada a objetos
Para aqueles que estão acostumados com JS, em linguagens orientadas a objeto não é possível atribuir funções a variáveis
Correção!
Não era possivel!
As Lambdas
mudaram esse contexto
Lambdas
Funções agora são valores
String nome = "Malaquias";
Integer idade = 27;
Double pi = 3.14;
Valores podem ser atribuídos a variáveis
public Integer somaMaisUm(Integer x){
return x + 1;
}
Essa é uma típica
função em Java
Vamos atribuí-la
a uma variável
Não precisaremos mais informar um nível de visibilidade
public Integer somaMaisUm(Integer x){
return x + 1;
}
Vamos atribuí-la
a uma variável
Não faz sentido a função ter um nome
public Integer somaMaisUm(Integer x){
return x + 1;
}
Vamos atribuí-la
a uma variável
O compilador do Java é inteligente o suficiente para não precisarmos dizer o tipo de retorno
public Integer somaMaisUm(Integer x){
return x + 1;
}
Vamos atribuí-la
a uma variável
Para transformar uma função em uma lambda adicionamos uma flecha em sua declaração
public Integer somaMaisUm(Integer x) -> {
return x + 1;
}
(Integer x) -> x + 1;
Vamos atribuí-la
a uma variável
Como só temos uma linha de código na função, podemos remover o return e as chaves
Function<Integer, Integer> somaMaisUm = (Integer x) -> {
return x + 1
};
Vamos atribuí-la
a uma variável
Agora podemos atribuí-la a uma variável do tipo Function
Function<Integer, Integer> somaMaisUm = (x) -> x + 1;
Vamos atribuí-la
a uma variável
O compilador já sabe qual o tipo do parâmetro da função então podemos remover a tipagem
var cinco = somaMaisUm.apply(5);
System.out.println(cinco);
Usando nossa
nova variável
Interfaces funcionais
Agora o bicho vai pegar
A maioria de
vocês sabem
o que é uma interface?
Toda classe define 2 itens:
-
o que uma classe faz
(as assinaturas dos métodos) -
como uma classe faz essas tarefas
(o corpo dos métodos e atributos privados)
Em orientação a objetos podemos criar um "contrato" que define tudo o que uma classe deve fazer se quiser ter um determinado status. Imagine:
contrato Autenticavel:
quem quiser ser Autenticavel precisa saber fazer:
1. dada uma senha, devolva um booleano
public interface Autenticavel {
boolean autentica(String senha);
}
Isso é um contrato
public class Usuario implements Autenticavel {
private String senha;
public boolean autentica(String senha) {
return this.senha.equalsIgnoreCase(senha);
}
}
Essa é uma implementação do contrato
Mas então qual a diferença entre uma interface e uma interface funcional?
Qualquer interface com um SAM
(Single Abstract Method) é uma interface
funcional
Mas a boa pratica recomenda que
se use a anotação
@FunctionalInterface
Métodos deafult não são abstratos e não contam, uma interface funcional pode ter vários desses métodos.
var cinco = somaMaisUm.apply(5);
System.out.println(cinco);
Vocês lembram disso?
Por que eu tive
que usar a função apply?
@FunctionalInterface public interface Function<T, R> { R apply(T t); default <V> Function<V, R> compose(Function<V, T> before){ Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } default <V> Function<T, V> andThen(Function<R, V> after){ Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } }
Nada te impede
de criar a sua propria interface funcional
@FunctionalInterface
public interface NumericSum {
int sum(int x, int y);
default boolean isPrime(int number) {
return number > 1 && IntStream
.range(2, number)
.noneMatch(i -> number % i == 0);
}
}
public static void main(String[] args) {
int numOne = 2;
int numTwo = 2;
NumericSum caclSum = (int x, int y) -> x + y;
if(caclSum.isPrime(numOne)) {
System.out.println(caclSum.sum(numOne, numTwo));
}
}
Assistam essa palestra
Dessa maneira conseguimos aplicar
o paradigma
funcional no Java
-
Funções puras
-
Funções de primeira classe (são valores)
-
Funções de primeira ordem (são parâmetros ou retorno)
-
Currying, composição e por aí vai..
Referências de métodos
Eu não gosto e
acho estranho...
Então leia,
esse texto
Streams
Um novo mundo
para as coleções
Um dos principais novidades do Java 8 foi o nascimento da biblioteca - java.util.stream - que contém bastante metodos para se trabalhar com coleções
for (String string : list) {
if (string.contains("Malaquias")) {
return true;
}
}
Com Streams nós saimos disso
boolean isExist = list
.stream()
.anyMatch(e -> {
e.contains("a")
});
Para isso....
Vamos ver na
pratica os métodos mais utilizados
List<String> list = List.of(...);
Stream<String> stream = list
.stream()
.filter(e -> e.contains("abc"));
O método filter () nos permite escolher o fluxo de elementos que satisfazem um predicado.
List<String> uris = List.of("C:\\My.txt");
Stream<Path> stream = uris
.stream()
.map(uri -> Paths.get(uri));
Podemos converter nossa Stream em
uma nova contendo novos elementos,
podemos usar o método map ():
List<String> list = List.of(...); List<String> newList = list .stream() .filter(e -> e.contains("abc") .collect(Collectors.toList());
Uma Stream vai sempre retornar uma nova Stream (imutabilidade) por isso precisamos trasnforma-la em coleções quando necessário.
Streams Parallel
Multi-threading nunca foi tão fácil
list
.parallelStream()
.map(element -> transform(element))
.collect(Collectors.toList());
list
.parallelStream()
.distinct()
.count();
OBRIGADO
POR NÃO
DORMIR
@mmalaquiasdev
Referências
Bons
estudos
Lambdas e Streams! Influencia funcional no Java
By Mateus Malaquias
Lambdas e Streams! Influencia funcional no Java
O Java 8 trouxe diversas funcionalidades, mas duas delas são muito impactantes na plataforma como um todo, não apenas na linguagem, mas também em seu compilador e na própria JVM. Vamos entender como usar e o que ganhamos com Lambdas e Streams.
- 321