Design, Functional, Java, OO, Programming

Iteração interna com Java 8 e métodos default

Iterando externamente

Vamos dizer que temos uma lista com nomes de personagens da Disney:

List<String> personagens = Arrays.asList("Pato Donald",
                            "Mickey", "Pateta", "Pluto");

Temos algumas opções para percorrer essa lista e imprimir o conteúdo. A mais intuitiva para iniciantes, é um for:

  for(int i = 0; i < personagens.size(); i++){
    String personagem = personagens.get(i);
    System.out.println(personagem);
  }

Uma opção mais interessante, mas que requer um pouco mais de conhecimento das bibliotecas do Java, é utilizar um Iterator:

  for(Iterator<String> it = personagens.iterator(); it.hasNext();){
    String personagem = it.next();
    System.out.println(personagem);
  }

Ambas as opções, se desconsiderarmos o uso de tipos genéricos, já funcionavam até o Java 4. Porém, do Java 5 em diante, percorrer uma lista ficou mais conciso através do for-each:

  for(String personagem : personagens){
    System.out.println(personagem);
  }

Mesmo com um for-each do Java 5, percorrer uma lista é algo tedioso e repetitivo. Quantas vezes um desenvolvedor Java experiente já escreveu um for desse tipo durante a carreira?

Com esse tipo de código, o desenvolvedor precisa se preocupar em “como” percorrer uma lista ao invés de focar apenas no mais importante: “o que” fazer com os elementos.

Essa maneira clássica, que deixa o “como” a cargo do desenvolvedor, pode ser chamada de iteração externa.

Iterando internamente

Com o Java 8, é possível focar apenas no “o que” fazer com os elementos da lista percorrida, utilizando o método forEach:

personagens.forEach(personagem -> System.out.println(personagem));

Observe que o método forEach pertence à própria lista e recebe um lambda.

Na verdade, pode ser passado qualquer lambda ou classe anônima que atenda à interface funcional Consumer, que recebe um argumento e não retorna resultado.

Poderíamos ter abreviado mais ainda o código utilizando uma referência ao método System.out.println:

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

Essa maneira nova, que permite ao desenvolvedor focar no “o que” fazer com os elementos, pode ser chamada de iteração interna.

Inserindo implementações em interfaces antigas com métodos default

O método forEach está definido em Iterable e, por consequência, em todos os List, Set e Queue da API de Coleções do Java. Porém, esse método não é abstrato.

O que iria acontecer se colocassem mais um método abstrato na interface Iterable? Código de várias bibliotecas e frameworks por aí, que fornececem suas próprias implementações para essas classes, iriam deixar de funcionar. E o Java tem como um de seus princípios mais fortes a retrocompatibilidade e evolução suave (para muitos, suave até demais).

Para evitar qualquer quebra inaceitável, foi criada uma implementação concreta na interface Iterable para o método forEach. Como foi possível inserir uma implementação em uma interface? Antes do Java 8, as interfaces só podiam ter métodos públicos e abstratos, afinal de contas…

Foi criado um novo mecanismo: os métodos default. Para definir um método default, criamos um método com a palavra-chave default e, em seguida, fornecemos uma implementação para o método.

Veja o que aconteceu com a interface Iterable:

package java.lang;

//imports ...

public interface Iterable<T> {

  Iterator<T> iterator(); //método implicitamente 
                          //abstrato e público 

  default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
      for (T t : this) {
        action.accept(t);
      }
  }

  //...
}

Métodos default permitem evoluir uma API antiga de maneira não disruptiva, sem romper com o passado. A implementação definida em uma método default é “copiada” para todas as classes que implementam essa interface. Em um próximo post, veremos com mais detalhes como funciona esse mecanismo.

Métodos default acabaram com a prática comum de ter uma interface acompanhada de uma superclasse abstrata que fornece implementações para a maioria dos métodos e deixando apenas detalhes para as subclasses concretas.

Um exemplo disso é o par List e AbstractList. Agora, se fossemos reprojetar a API de Coleções do Java, poderíamos colocar boa parte do código de AbstractList como métodos default da interface List.

O código desse post pode ser encontrado em: https://gist.github.com/alexandreaquiles/9753849

Anúncios
Design, Functional, Java, OO, Programming

Menos Cerimônia e Mais Essência com Java 8

Recentemente, foi lançada a versão 8 do Java, com mudanças significativas na linguagem.

Mas qual é a razão pra tanta algazarra na Internet?

Com Java 8, é possível escrever código com menos cerimônia e mais essência.

Filtrando com Classes Anônimas no Java 7 ou anterior

No post anterior, vimos como melhorar nosso código utilizando o molde Strategy e classes anônimas.

Abstraímos, em uma classe chamada FiltroDeDesenhos, as operações de percorrer a lista, chamar a comparação e acumular os elementos uma nova lista. Já o contrato da lógica de comparação foi definido em uma interface chamada ComparacaoDeDesenhos.

O código resultante, que utiliza a modelagem acima para filtrar uma lista de desenhos, ficou assim:

FiltroDeDesenhos filtro = new FiltroDeDesenhos();

List<Desenho> antesDe1960 = filtro.filtra(desenhos, 
	new ComparacaoDeDesenhos() {
		public boolean valePara(Desenho desenho) {
			return desenho.getDecadaDeCriacao() < 1960;
		}
});
List<Desenho> comecamComS = filtro.filtra(desenhos, 
	new ComparacaoDeDesenhos() {
		public boolean valePara(Desenho desenho) {
			return desenho.getNome().startsWith("S");
		}
});

Focando no essencial com Lambdas do Java 8

As linhas destacadas no código acima são o realmente essencial: definem a lógica de filtragem. O resto todo faz parte da sintaxe assustadora de classes anônimas do Java.

A partir do Java 8, é possível simplificarmos nosso código com o uso de lambdas:

List<Desenho> antesDe1960 = filtro.filtra(desenhos,
	(Desenho desenho) -> {
		return desenho.getDecadaDeCriacao() < 1960;
	}
);
List<Desenho> comecamComS = filtro.filtra(desenhos,
	(Desenho desenho) -> {
		return desenho.getNome().startsWith("S");
	}
);

Lambdas são métodos anônimos que podem ser usados no lugar de classes anônimas, com uma sintaxe bem mais enxuta. Internamente, são implementadas com um mecanismo diferente das classes anônimas. Estão presentes na maioria das linguagens e são uma ótima nova adição à linguagem Java.

Só foi possível utilizar lambdas no lugar de classes anônimas no nosso caso porque nossa interface ComparacaoDeDesenhos é uma interface funcional: possui apenas um método abstrato.

Veja:

interface ComparacaoDeDesenhos {
	boolean valePara(Desenho desenho);
}

É possível marcar nossa interface com a anotação @FunctionalInterface, porém não é algo obrigatório. Essa anotação é usada pelo compilador para lançar um erro assim que um segundo método abstrato for declarado.

Podemos ainda omitir as chaves que definem o bloco e o return, já que o nosso lambda possui apenas uma expressão. Podemos também omitir a classe do parâmetro, por causa do mecanismo de inferência de tipos do Java 8, além dos parênteses:

List antesDe1960 = filtro.filtra(desenhos,
	desenho -> desenho.getDecadaDeCriacao() < 1960
);
List comecamComS = filtro.filtra(desenhos,
	desenho -> desenho.getNome().startsWith("S")
);

Evitando código desnecessário com Streams do Java 8

Podemos ir além e remover a nossa classe FiltroDeDesenhos e a interface ComparacaoDeDesenhos, utilizando um Stream, um novo recurso do Java 8:

Stream<Desenho> streamDeDesenhos = desenhos.stream();

Stream<Desenho> streamDosAntesDe1960 = 
    streamDeDesenhos
        .filter(desenho -> desenho.getDecadaDeCriacao() < 1960);

Stream<Desenho> streamDosQueComecamComS = 
    streamDeDesenhos
        .filter(desenho -> desenho.getNome().startsWith("S"));

Criamos um Stream a partir da nossa lista de desenhos invocando o método stream.

Um Stream é uma sequência de elementos que permitem a execução de várias operações. Uma delas é o filter, que recebe um lambda que tem a comparação e retorna um outro Stream que contém apenas os elementos em que a condição da comparação é válida.

O lambda (ou classe anônima) recebido pelo filter deve respeitar o contrato definido pela interface funcional Predicate. Essa interface recebe um objeto e retorna um booleano, de maneira parecida com a nossa interface ComparacaoDeDesenhos, mas mais genérica.

Mas o que podemos fazer com o Stream dos elementos filtrados? Podemos transformá-lo numa lista com .collect(Collectors.toList()) ou imprimir os elementos com:

streamDosAntesDe1960
	.forEach(System.out::println);
streamDosQueComecamComS
	.forEach(System.out::println);

Invocamos o método forEach passando um referência ao método println de System.out. Isso é equivalente ao lambda desenho -> System.out.println(desenho).

Juntando tudo

Pegando o código acima, explicitando a criação da lista e omitindo os streams intermediários, temos:

class ProgramaComStreams {
    public static void main(String[] args) {
        Desenho popeye = new Desenho("Popeye", 1920);
        Desenho picaPau = new Desenho("Pica-pau", 1940);
        Desenho flintstones = new Desenho("Flintstones", 1960);
        Desenho scoobyDoo = new Desenho("Scooby-Doo", 1970);
        Desenho simpsons = new Desenho("Simpsons", 1990);

        List<Desenho> desenhos = Arrays.asList(popeye, picaPau, 
                                        flintstones, scoobyDoo, simpsons);

        desenhos.stream()
	    .filter( desenho -> desenho.getDecadaDeCriacao() < 1960 )
            .forEach(System.out::println);
		
        System.out.println("---------------");
		
        desenhos.stream()
            .filter( desenho -> desenho.getNome().startsWith("S") )
            .forEach(System.out::println);
	}
}

Após executarmos o código acima, teríamos a seguinte saída:

Popeye (1920)
Pica-pau (1940)
---------------
Scooby-Doo (1970)
Simpsons (1990)

Comparando as implementações

Focando na filtragem dos desenhos anteriores a 1960, vamos comparar as implementações.

Primeiro, vamos lembrar da primeira versão, sem Strategy nem classes anônimas:

List<Desenho> antesDe1960 = new ArrayList<>();
for (Desenho desenho : desenhos) {
	if(desenho.getDecadaDeCriacao() < 1960){
		antesDe1960.add(desenho);
	}
}
for (Desenho desenho : antesDe1960) {
	System.out.println(desenho);
}

Agora, o código que cria uma implementação anônima da interface ComparacaoDeDesenhos:

List<Desenho> antesDe1960 = filtro.filtra(desenhos, 
	new ComparacaoDeDesenhos() {
		public boolean valePara(Desenho desenho) {
			return desenho.getDecadaDeCriacao() < 1960;
		}
});
for (Desenho desenho : antesDe1960) {
	System.out.println(desenho);
}

Então, usando lambas, temos uma sintaxe mais sucinta para definir a lógica de comparação:

List antesDe1960 = filtro.filtra(desenhos,
	desenho -> desenho.getDecadaDeCriacao() < 1960
);
for (Desenho desenho : antesDe1960) {
	System.out.println(desenho);
}

Finalmente, usando streams, deixamos de lado classes desnecessárias, além de imprimir os desenhos de maneira concisa:

desenhos.stream()
	.filter( desenho -> desenho.getDecadaDeCriacao() < 1960 )
	.forEach(System.out::println);

Compare os códigos e observe que, com lambdas e streams, a cerimônia do Java foi radicalmente diminuída. Podemos focar na essência da nossa tarefa: a lógica de filtragem.

Puxa… Achei confuso. E agora?

Realmente, utilizar lambdas, streams e as outras mudanças do Java 8 não é algo fácil. É preciso um bocado de estudo e de prática para entender e saber quando usar esses novos recursos.

Aqui, os conceitos foram vistos de maneira bem superficial. É importante estudá-los de maneira aprofundada. Há ainda muito mais coisas interessantes no Java 8: métodos default e estáticos em interfaces, optionals, streams paralelos, a nova API de datas, etc.

O livro Java 8 Prático, da Casa do Código é uma ótima referência. As novidades do Java 8 são estudadas com detalhes. Tive o prazer de participar da revisão técnica e recomendo! Os autores do livro também escreveram um post em que passeiam pelas funcionalidades mais importantes dessa nova versão do Java.

Não deixe de baixar a JDK 8.

O código desse post pode ser encontrado em: https://gist.github.com/alexandreaquiles/9649033

Functional, Java, Programming, Scala

Sequências Infinitas em Scala e Java

Sequências Infinitas?

Em um workshop com o pessoal do trabalho, iríamos começar mais uma implementação de Fibonacci.

Acabamos definindo uma API parecida com a abaixo:

public List<Integer> fibonacci();

Porém, para conseguir fazer algo do gênero, é necessária uma maneira de representar uma sequência infinita de valores.

No workshop, como precisávamos de foco, mudamos a API para receber um número e retornar o número de Fibonacci correspondente.

Só que há uma maneira de representar essa sequência infinita em um computador…

Fibonacci?

Só para lembrar,  conforme está descrito na Wikipedia, a sequência de Fibonacci “é uma sequência de números naturais, na qual os primeiros dois termos são 0 e 1, e cada termo subsequente corresponde à soma dos dois precedentes”.

Lazy Evaluation

Na teoria de linguagens de programação, há o conceito de Lazy Evaluation, em que o runtime adia a avaliação de um trecho de código até quando esse código realmente precisar ser executado.

Esse conceito está presente principalmente em Programação Funcional, em linguagens como Haskell, Scheme ou Scala.

Em Scala

Com a linguagem de programação Scala, é possível criar uma  uma função recursiva que calcula a sequência de Fibonacci em que não há condição de parada!

def fibonacciSequence : Stream[Long] = {
    def fibonacciFrom(a: Long, b: Long) : Stream[Long] = a #:: fibonacciFrom(b, a+b)
    fibonacciFrom(0, 1)
}

Mas como isso é possível? Como o código acima não entra em recursão infinita até gerar um StackOverflowError?

No código acima, é utilizada uma estrutura de dados chamada Stream, que é uma lista cuja avaliação da concatenação (o #::) é lazy. Por isso, a chamada recursiva só é feita quando realmente é necessária.

Se chamarmos diretamente a função fibonacciSequence a partir do REPL do Scala será retornado:

scala> fibonacciSequence
> res0: scala.collection.immutable.Stream[Long] = Stream(0, ?)

Observe que só o primeiro valor, que no caso é 0, será retornado. O resto da sequência ainda não foi avaliado, o que é representado como ?.

Se utilizarmos a função take para pegarmos os 5 primeiros valores dessa sequência, ainda assim apenas o primeiro valor é avaliado:

scala> fibonacciSequence take 5
> res1: scala.collection.immutable.Stream[Long] = Stream(0, ?)

Para realmente avaliar os valores, temos que utilizá-los, por exemplo, tranformando para uma lista que não é lazy com um toList ou percorrendo-os com um foreach:

scala> fibonacciSequence take 5 toList
> res2: List[Long] = List(0, 1, 1, 2, 3)

scala> fibonacciSequence take 5 foreach println
> 0
| 1
| 1
| 2
| 3

Em Java

Recentemente, o polêmico Uncle Bob tem escrito sobre como utilizar conceito de Programação Funcional em Java e tem recebido diversas críticas.

Em um de seus artigos, Uncle Bob mostrou como utilizar lazy evaluation em Java utilizando um Iterator. Há um exemplo mostrando o uso de Iterator para criar uma sequência infinita de inteiros elevados ao quadrado.

Na verdade, Neal Ford já havia mostrado como usar um Iterator para obter lazyness com Java. O exemplo utilizado foi o cálculo de números primos.

Com Iterator

Através da técnica do Iterator, é possível fazer um código bem simples para gerar uma sequância “infinita” com os números de Fibonacci:

public class FibonacciSequence implements Iterator<Long> {
    private long n = 0;
    private long a = 0;
    private long b = 1;

    public boolean hasNext() { return a + b > 0; }

    public Long next() {
        long fib;
        if(n == 0 || n == 1) {
            fib = n;
        }
        else {
            fib = a + b;
            a = b;
            b = fib;
        }
        n++;
        return fib;
    }

    public void remove() {}
}

As variáveis a e b armazenam os valores de Fibonacci dos números anteriores. O número atual é mantido na variável n.

Os dois primeiros valores da sequência 0 e 1, são retornados diretamente, sem atualizar as variáveis a e b.

O Fibonacci de um dado número n é a soma dos números de Fibonacci precedentes, portando é a + b.  Para preparar a sequência para os próximos valores, as variáveis a e b são atualizadas com o número de Fibonacci anterior e o atual, respectivamente.

A implementação do método hasNext considera se a soma de a + b ainda é positiva, para evitar Integer Overflow, o que faria com que os números a e b ficassem com valores negativos.

Podemos utilizar o código acima, por exemplo, para pegar os 10 primeiros números na sequência:

FibonacciSequence fs = new FibonacciSequence();
for(int i = 0; i < 10 && fs.hasNext(); i++){
    System.out.println(fs.next());
}

Podemos criar o objeto Taker criado por Uncle Bob para simular a função take do Scala:

public class Taker<T> {
    public List<T> take(int n, Iterator<T> xs) {
        List<T> taken = new ArrayList<T>();
        for (int i = 0; xs.hasNext() && i < n; i++) {
           taken.add(xs.next());
        }
        return taken;
    }
}

Se utilizarmos o Taker para gerar a sequência dos 25 primeiros números de Fibonacci, teríamos o seguinte código:

List<Long> fibonacciUpto25 = new Taker<Long>().take(25, new FibonacciSequence());

Com Functional Java

A biblioteca Functional Java tem uma estrutura de dados Stream parecida com a do Scala, que permite lazy evaluation.

Baseados em um exemplo da documentação da biblioteca, poderíamos definir uma variável no nosso programa que contém a sequência de Fibonacci:

public static final Stream<Long> fibonacciSequence = new F2<Long, Long, Stream<Long>>() {
    public Stream<Long> f(final Long a, final Long b) {
        return Stream.cons(a, curry().f(b).lazy().f(a + b));
    }
}.f(0L, 1L);

O método cons da classe Stream é correspondente ao #:: do Scala.

A classe Stream provê um método take. Assim, para obter nossa lista com os 25 primeiros números da sequência de Fibonacci:

List<Long> fibonacciUpto25 = fibonacciSequence.take(25).toList();

Com Totally Lazy

Já a biblioteca Totally Lazy tem uma função embutida na classe Numbers que gera a sequência de Fibonacci. Essa função é implementada da seguinte forma:

public static Sequence<Number> fibonacciSequence  =
computation(Pair.<Number, Number> pair(0, 1), reduceLeftShift(sum)).map(first(Number.class));

Para obter nossos 25 primeiros números de Fibonacci:

List<Number> fibonacciUpto25 = fibonacciSequence.take(25).toList();

A abordagem do Totally Lazy é bem diferente das demais.  Observe que a classe Number é utilizada, ao invés de Long.