conferência internacional de desenvolvimento de software - … · 2015. 5. 27. · manipula...

Post on 27-Nov-2020

2 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Código funcional em Java: Superando o hype

Eder Ignatowicz @ederign

Lambda Expressions 101

Dora

Bento

<3

Jesse "Pinkman"

public Pug( String nome, String color, Integer size ) { this.nome = nome; this.color = color; this.weight = size; }

Pug dora = new Pug( "Dora", "abricot", 10 ); Pug bento = new Pug( "Bento", "abricot", 13 ); Pug jesse = new Pug( "Jesse", "black", 9 );

Requisito:

Filtrar todos os pugs de cor “abricot" da lista

Filtrar todos os pugs de cor “abricot" da lista

private static List<Pug> filterAbricotPugs( List<Pug> pugs ) { List<Pug> abricots = new ArrayList<>(); for ( Pug pug : pugs ) { if ( pug.getColor().equals( "abricot" ) ) { abricots.add( pug ); }

} return abricots; }

List<Pug> pugs = Arrays.asList( dora, bento, jesse );

List<Pug> abricot = filterAbricotPugs( pugs );

Pug{nome='Dora', color='abricot', weight=10} Pug{nome='Bento', color='abricot', weight=13}

Novo Requisito: :)

Filtrar todos os pugs de cor “black" da lista

private static List<Pug> filterAbricotPugs( List<Pug> pugs ) { List<Pug> abricots = new ArrayList<>(); for ( Pug pug : pugs ) { if ( pug.getColor().equals( "abricot" ) ) { abricots.add( pug ); }

} return abricots; }

private static List<Pug> filterBlackPugs( List<Pug> pugs ) { List<Pug> abricots = new ArrayList<>(); for ( Pug pug : pugs ) { if ( pug.getColor().equals( "black" ) ) { abricots.add( pug ); }

} return abricots; }

Filtrar todos os pugs de cor “black" da lista private static List<Pug> filterPugByColor( List<Pug> pugs, String color ) { List<Pug> coloured = new ArrayList<>(); for ( Pug pug : pugs ) { if ( pug.getColor().equals( color ) ) { coloured.add( pug ); }

} return coloured;

}

List<Pug> black = filterPugByColor( pugs, "black" );

black.forEach( System.out::println );

Pug{nome='Jesse', color='black', weight=9}

Novo Requisito: :)

Filtrar todos os pugs com sobrepeso (>10 kg)

Filtrar todos os pugs com sobrepeso (>10 kg)

private static List<Pug> filterPugBySize( List<Pug> pugs, int size ) { List<Pug> fat = new ArrayList<>(); for ( Pug pug : pugs ) { if ( pug.getWeight() > size ) { fat.add( pug ); }

} return fat; }

List<Pug> fat = filterPugBySize( pugs, 10 );

fat.forEach( System.out::println );

Pug{nome='Bento', color='abricot', weight=13}

private static List<Pug> filterPugs( List<Pug> pugs, String color, int weight, boolean flag ) { List<Pug> result = new ArrayList<>(); for ( Pug pug : pugs ) { if ( ( flag && pug.getColor().equals( color ) ) || ( !flag && pug.getWeight() > weight ) ) { result.add( pug ); }

} return result; }

Refatorador :D

List<Pug> result = filterPugs(pugs, "black", 0, true); List<Pug> result1 = filterPugs(pugs, "", 10, false);

Behavior Parametrization

Behavior Parametrization public interface PugPredicate { boolean test(Pug pug); }

class BlackPugPredicate implements PugPredicate{

@Override public boolean test( Pug pug ) { return pug.getColor().equals( "black" ); } }

class FatPugPredicate implements PugPredicate{

@Override public boolean test( Pug pug ) { return pug.getWeight()>10; } }

Predicate

List<Pug> black = filterPug( pugs, new BlackPugPredicate() );

List<Pug> fat = filterPug( pugs, new FatPugPredicate() );

fat.forEach( System.out::println );

Pug{nome='Bento', color='abricot', weight=13}

Problemas?

Classes anônimas <3

List<Pug> abricotsNotFat =

filterPug( pugs, new PugPredicate() { @Override public boolean test( Pug pug ) { return pug.getColor().equals( "abricot" ) && pug.getWeight() <= 10; } } );

abricotsNotFat.forEach( System.out::println );

Pug{nome='Dora', color='abricot', weight=10}

Expressões Lambda <3

(Pug p1,Pug p2) -> p1.getWeight().compareTo(p2.getWeight())

Parâmetros do

Lambda

"Arrow"

Corpo do

Lambda

class BlackPugPredicate implements PugPredicate{

@Override public boolean test( Pug pug ) { return "black".equals( pug.getColor() ) ); } }

blacks = filterPug( pugs, new BlackPugPredicate() );

blacks = filterPug( pugs,

(Pug pug) ->"black".equals( pug.getColor() ) );

Posso modificar mais um pouco?

public interface Predicate<T>{ boolean test(T t);}

public static <T> List<T> filter (List<T> list, Predicate<T> p){

List<T> result = new ArrayList<>(); for(T e: list){

if(p.test(e)){ result.add(e);

} }

return result;

}

List<Pug> blacks =

filter( pugs,

(Pug pug) ->"black".equals( pug.getColor() ) );

List<String> pares =

filter(numbers,

(Integer i) -> i % 2 == 0);

Predicate<Pug> fatPredicate = d -> d.getWeight() > 9; Predicate<Pug> abricotPredicate1 = d -> d.getColor().equalsIgnoreCase( "abricot" ); Predicate<Pug> abricotAndFat = abricotPredicate.and( fatPredicate );

@FunctionalInterface public interface Predicate<T>{ boolean test(T t); }

public static <T> List<T> filter(List<T> list, Predicate<T> p) { List<T> results = new ArrayList<>(); for(T s: list){ if(p.test(s)){ results.add(s); } } return results; }

Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty(); List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);

Predicate

@FunctionalInterface public interface Function<T, R>{ R apply(T t); } public static <T, R> List<R> map(List<T> list, Function<T, R> f) { List<R> result = new ArrayList<>(); for(T s: list){ result.add(f.apply(s)); } return result; }

List<Integer> l = map(Arrays.asList("qcon","em","sampa"),

(String s) -> s.length() ); // [4, 2, 5]

Function

Etc. etc. etc.

BiConsumer BiFunction

BinaryOperator BiPredicate

BooleanSupplier Consumer

DoubleBinaryOperator DoubleConsumer DoubleFunction DoublePredicate DoubleSupplier

DoubleToIntFunction DoubleToLongFunction DoubleUnaryOperator

Function IntBinaryOperator

IntConsumer IntFunction IntPredicate IntSupplier

IntToDoubleFunction IntToLongFunction IntUnaryOperator

LongBinaryOperator LongConsumer LongFunction LongPredicate LongSupplier

LongToDoubleFunction LongToIntFunction

LongUnaryOperator ObjDoubleConsumer

ObjIntConsumer ObjLongConsumer

Predicate Supplier

ToDoubleBiFunction ToDoubleFunction

ToIntBiFunction ToIntFunction

ToLongBiFunction ToLongFunction UnaryOperator

(Pug p1,Pug p2) -> "black".equals( pug.getColor() )

Parâmetros do

Lambda

"Arrow"

Corpo do

Lambda

Código funcional em Java: Superando o hype

Eder Ignatowicz

Streams API

Streams: Manipula coleções de forma declarativa

Quais são os nomes dos Pugs com peso maior do que 9 quilos?

Streams: Manipula coleções de forma declarativa

SELECT nome FROM pugs WHERE weight < 9 order by peso.

Em JavaList<Pug> gordinhos = new ArrayList<>(); for ( Pug pug : pugs ) { if ( pug.getWeight() > 9 ) { gordinhos.add( pug ); } } Collections.sort( gordinhos, new Comparator<Pug>() { @Override public int compare( Pug p1, Pug p2 ) { return Integer.compare( p1.getWeight(), p2.getWeight() ); } });

List<String> nomeGordinhos = new ArrayList<>(); for ( Pug pug : gordinhos ) { nomeGordinhos.add( pug.getNome() ); }

Java 8Streams API

Em JavaList<String> gordinhosNome =

pugs.stream()

.filter( d -> d.getWeight() > 9 )

.sorted( comparing( Pug::getWeight ) )

.map( Pug::getNome )

.collect( toList() );

Seleciona > 9 kg

Ordena por peso

Extrai o nomeArmazena em uma lista

Em JavaList<Pug> gordinhos = new ArrayList<>(); for ( Pug pug : pugs ) { if ( pug.getWeight() > 9 ) { gordinhos.add( pug ); } } Collections.sort( gordinhos, new Comparator<Pug>() { @Override public int compare( Pug p1, Pug p2 ) { return Integer.compare( p1.getWeight(), p2.getWeight() ); } });

List<String> nomeGordinhos = new ArrayList<>(); for ( Pug pug : gordinhos ) { nomeGordinhos.add( pug.getNome() ); }

Em JavaList<String> gordinhosNome =

pugs.parallelStream()

.filter( d -> d.getWeight() > 9 )

.sorted( comparing( Pug::getWeight ) )

.map( Pug::getNome )

.collect( toList() );

Seleciona > 9 kg

Ordena por peso

Extrai o nomeArmazena em uma lista

(

Parallel streams não são mágica!

)

Stream Pipelines

filterpugs -> —> sorted —> map —> collect

lambda lambda lambda

Código: Declarativo

Componentizável Paralelizável

Streams API

Sequência de elementos de uma fonte que suporta operações de processamento em seus dados

Streams

Sequência de elementos de uma fonte que suporta operações de processamento em seus dados

Streams

Sequência de elementos de uma fonte que suporta operações de processamento em seus dados

Streams

Sequência de elementos de uma fonte que suporta operações de

processamento em seus dados

Streams

Streams

Uma fonte de dados para a query Uma cadeia de operações intermediárias

(pipeline) Uma operação terminal que gera o resultado

Vamos a prática

public Venda( Vendedor vendedor, int ano, int valor ) { this.vendedor = vendedor; this.ano = ano; this.valor = valor; }

public Vendedor( String nome, String cidade ) { this.nome = nome; this.cidade = cidade; }

Vendedor eder = new Vendedor("Eder", "Campinas"); Vendedor pedro = new Vendedor("Pedro", "Apucarana"); Vendedor luciano = new Vendedor("Luciano", "Piracicaba"); Vendedor junior = new Vendedor("Junior", "Londrina");

List<Venda> transactions = Arrays.asList( new Venda( eder, 2014, 100 ), new Venda( eder, 2013, 200 ), new Venda( pedro, 2014, 300 ), new Venda( luciano, 2012, 500 ), new Venda( luciano, 2012, 400 ), new Venda( junior, 2012, 500 ));

Quais são as vendas que fizemos em 2014? Ordenadas?

List<Venda> vendas2014 = transactions .stream()

List<Venda> vendas2014 = transactions .stream() .filter( venda -> venda.getAno() == 2014 )

List<Venda> vendas2014 = transactions .stream() .filter( venda -> venda.getAno() == 2014 ) .sorted( comparing( Venda::getValor ) )

List<Venda> vendas2014 = transactions .stream() .filter( venda -> venda.getAno() == 2014 ) .sorted( comparing( Venda::getValor ) ) .collect( toList() );

List<Venda> vendas2014 = transactions .stream() .filter( venda -> venda.getAno() == 2014 ) .sorted( comparing( Venda::getValor ) ) .collect( toList() );

vendas2014.forEach(System.out::println);

Venda{vendedor=Vendedor{nome='Eder', cidade='Campinas'}, ano=2014, valor=100}

Venda{vendedor=Vendedor{nome='Pedro', cidade='Apucarana'}, ano=2014, valor=300}

Em que cidades temos vendedores?

List<String> cidadesAtendidas = vendas.stream() .map( venda -> venda.getVendedor().getCidade() ) .distinct() .collect( toList() );

Campinas Apucarana Piracicaba Londrina

Qual foi a maior venda?

Optional<Integer> maiorVenda = vendas.stream() .map(Venda::getValor) .reduce( Integer::max );

maiorVenda.ifPresent( i -> System.out.println(i));

500

Total de vendas?

Optional<Integer> totalVendas = vendas.stream() .map(Venda::getValor) .reduce( Integer::sum );

totalVendas.ifPresent( i -> System.out.println(i));

2000

Quais são as vendas de cada vendedor? Ordenadas?

Map<Vendedor, List<Venda>> vendedorPorVendas = vendas.stream() .sorted( comparing( Venda::getValor ) ) .collect( groupingBy( Venda::getVendedor ) );

System.out.println(vendedorPorVendas);

{Vendedor{nome='Junior', cidade='Londrina'}=[Venda{vendedor=Vendedor{nome='Junior', cidade='Londrina'}, ano=2012, valor=500}],

Vendedor{nome='Pedro', cidade='Apucarana'}=[Venda{vendedor=Vendedor{nome='Pedro', cidade='Apucarana'}, ano=2014, valor=300}],

Vendedor{nome='Luciano', cidade='Piracicaba'}=[Venda{vendedor=Vendedor{nome='Luciano', cidade='Piracicaba'}, ano=2012, valor=400}, Venda{vendedor=Vendedor{nome='Luciano', cidade='Piracicaba'}, ano=2012, valor=500}],

Vendedor{nome='Eder', cidade='Campinas'}=[Venda{vendedor=Vendedor{nome='Eder', cidade='Campinas'}, ano=2014, valor=100}, Venda{vendedor=Vendedor{nome='Eder', cidade='Campinas'}, ano=2013, valor=200}]}

Streams são lazy

Stream Pipelines

filterpugs -> —> map —> collect

lambda lambda

Stream Pipelines

intermediatepugs -> —> —> findFirst

lambda lambda

intermediate

Lazy Streams

List<Dog> dogs = Arrays.asList( new Dog( "Dora", true, 10, Dog.RACA.PUG ), new Dog( "Bento", true, 13, Dog.RACA.PUG ), new Dog( "Rex", false, 8, Dog.RACA.SRD ), new Dog( "Teté", false, 6, Dog.RACA.SRD ), new Dog( "Banzé", true, 7, Dog.RACA.SRD ), new Dog( "Rin-Tin-Tin", false, 15, Dog.RACA.PASTOR ) );

Qual o nome do primeiro SRD que pesa mais do que 5kg?

String nomePrimeiroSRDMaiorDoQue5Kg = dogs.stream() .filter( dog -> { System.out.println( "filter - " + dog.getNome() ); return dog.getRaca().equals( Dog.RACA.SRD) && dog.getPeso() > 5; }) .map( dog -> { System.out.println( "map - " + dog.getNome() ); return dog.getNome(); }) .findFirst() .get();

Eager Streams

Dora Bento

Rex Teté

Banzé Rin Tin tin

—>SRD e

peso> 5

filter map

Rex Teté

Banzé —> Nome

"Rex" “Teté"

“Banzé" —> —> Nome

findFirst

“Rex" —>—>

Lazy Stream

Dora Bento

Rex Teté

Banzé Rin Tin tin

—>SRD e

peso> 5

filter map

Rex—> Nome “Rex"—> —> first()

findFirst

“Rex" —>—>

String nomePrimeiroSRDMaiorDoQue5Kg = dogs.stream() .filter( dog -> { System.out.println( "filter - " + dog.getNome() ); return dog.getRaca().equals( Dog.RACA.SRD) && dog.getPeso() > 5; }) .map( dog -> { System.out.println( "map - " + dog.getNome() ); return dog.getNome(); }) .findFirst() .get();

filter - Dora filter - Bento filter - Rex map - Rex

Rex

List<Dog> dogs = Arrays.asList( new Dog( "Dora", true, 10, Dog.RACA.PUG ), new Dog( "Bento", true, 13, Dog.RACA.PUG ), new Dog( "Rex", false, 8, Dog.RACA.SRD ), new Dog( "Teté", false, 6, Dog.RACA.SRD ), new Dog( "Banzé", true, 7, Dog.RACA.SRD ), new Dog( "Rin-Tin-Tin", false, 15, Dog.RACA.PASTOR ) );

Streams "infinitos"

Criar uma lista de números primos infinita

public static boolean isPrime( final int number ) { return number > 1 && IntStream.rangeClosed( 2, (int) Math.sqrt( number ) ) .noneMatch( divisor -> number % divisor == 0 ); }

public static int primeAfter( final int number ) { if ( isPrime( number + 1 ) ) { return number + 1; } else { return primeAfter( number + 1 ); } }

Coleções "infinitas"

public static List<Integer> primes(final int fromNumber, final int count) { return Stream.iterate(primeAfter(fromNumber - 1), Primes::primeAfter) .limit(count) .collect(Collectors.<Integer>toList()); }

public static List<Integer> primes(final int fromNumber, final int count) { return Stream.iterate(primeAfter(fromNumber - 1), Primes::primeAfter) .limit(count) .collect(Collectors.<Integer>toList()); }

System.out.println("10 primos do 1: " + primes( 1, 10 )); System.out.println("10 primos do 1000: " + primes( 1000, 10 ));

10 primos do 1: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] 10 primos do 1000: [1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061]

Recursões

Fibonacci

public static Long fib( int n ) { if ( n < 2 ) { return new Long( n ); } else { return fib( n - 1 ) + fib( n - 2 ); } }

Memoization

Pure FunctionsIn computer programming, a function may be described as a pure function if both these statements about the function hold:

1-) The function always evaluates the same result value given the same argument value(s). The function result value cannot depend on any hidden information or state that may change as program execution proceeds or between different executions of the program, nor can it depend on any external input from I/O devices (usually—see below.

2-) Evaluation of the result does not cause any semantically observable side effect or output, such as mutation of mutable objects or output to I/O devices (usually—see below)

Manual

Integer doubleValue(Integer x) { return x * 2; }

Integer doubleValue(Integer x) { if (cache.containsKey(x)) { return cache.get(x); } else { Integer result = x * 2; cache.put(x, result) ; return result; } }

private Map<Integer, Integer> cache = new ConcurrentHashMap<>();

public Integer fib(int n) { if (n == 0 || n == 1) return n;

Integer result = cache.get( n );

if (result == null) { synchronized (cache) { result = cache.get(n);

if (result == null) { result = fib(n - 2) + fib(n - 1); cache.put(n, result); } } }

return result; }

Java 8 to the rescue

public class Fibonacci {

private Map<Integer, Integer> cache = new ConcurrentHashMap<>();

public Long fib(int n) { if (n == 0 || n == 1) return n;

return cache.computeIfAbsent(n, (k) -> fib(k - 2) + fib(k - 1)); }

}

DEMO

Automática

Function<Integer, Integer> f = x -> x * 2;

Function<Integer, Integer> g = Memoizer.memoize(f);

public class Memoizer {

public static <T, R> Function<T, R> memoize( Function<T, R> fn ) { Map<T, R> map = new ConcurrentHashMap<T, R>(); return ( t ) -> map.computeIfAbsent( t, fn ); }

}

static Integer longCalculation(Integer x) { try { Thread.sleep(1_000); } catch (InterruptedException ignored) { } return x * 2; }

static Function<Integer, Integer> loooongCalc = Memoizer::longCalculation;

static Function<Integer, Integer> loooongCalcMemo = Memoizer.memoize(loooongCalc);

loooongCalcMemo.apply( 30 ); 1005ms

loooongCalcMemo.apply( 30 ); 0 ms

static Function<Integer, Long> fibonacci = Fibonacci::calc;

static Function<Integer, Long> memoFib = Memoizer.memoize( fibonacci );

memoFib.apply( 30 ); 832040 56ms

memoFib.apply( 30 ); 832040 0 ms

E agora?

Programar funcional em Java é uma mudança de paradigma

Java é multi-paradigma

Imperativo, OO e Funcional

Escolha o melhor deles para o seu problema

Java 8, Lambdas e Streams trouxeram uma nova vida

para o Java

DIVIRTA-SE!

Obrigado!!!

@ederign

top related