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.

 

Eu

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