sexta-feira, 30 de agosto de 2013

Programação funcional para programadores Java

Em um post anterior comecei uma série sobre Scala, focando em programadores Java. Porém notei que a curva de aprendizado de uma linguagem, principalmente em novo paradigma pode ser drástica demais. Portanto decidi focar mais no aprendizado do paradigma aproveitando o que puder do Java. Apesar de Java não ser funcional podemos aprender algumas técnicas interessantes e melhorar nossos projetos atuais.

Uma característica importante do paradigma funcional é o projeto dos tipos das linguagens. O uso de tipos como listas, mapas, árvores e conjuntos são enfatizados para a manipulação de dados. Nas linguagens mais puras os valores são imutáveis, logo as variáveis devem ser inicializadas com valores válidos. Note que isto é muito importante. Se precisamos inicializar nossas variáveis com valores válidos isso significa que nunca teremos variáveis nulas. O próprio inventor do conceito de nulo reconhece que isso é um erro.

Caso ainda esteja resistente a reconhecer este erro, lembre-se de quantas NullPointerException seu código de produção gerou, ou ao menos quanta complexidade foi adicionada ao código através de condicionais você teve de fazer verificando se algo é diferente de null.

Claro que podemos apelar para o Null Object Pattern, mas nem sempre é trivial e envolve muito trabalho em alguns casos.

O ideal seria o sistema de tipos da nossa linguagem nos ajudar com isso. Vamos ver um exemplo:

public abstract class Option<T> {
public abstract boolean hasValue();
public abstract T get();
public T getOrElse(T alternative) {
return hasValue() == true ? get() : alternative;
}
}
view raw Option.java hosted with ❤ by GitHub
Option é uma classe abstrata que representa um tipo que pode ou não ter valor. Teremos mais duas subclasses, uma indicado a existência de valor e outro a ausência. O método hasValue() informará isso para nós através de sua implementação em cada subclasse. O método get() retornar o valor, caso este exista e por fim getOrElse(T alternative) retorna o valor da Option, caso este seja inexistente é retornada a alternativa passada como parâmetro. As coisas ficarão mais claras com a implementação das subclasses e uso.

Vamos ao subtipo que indica existência de valor:

public final class Some<T> extends Option<T> {
private final T value;
public Some(T value) { this.value = value; }
public boolean hasValue() { return true; }
public T get() { return value; }
@Override
public String toString() { return "Some("+value+")"; }
@Override
public boolean equals(Object other) {
if (other == null || other.getClass() != Some.class)
return false;
Some<?> that = (Some<?>) other;
Object thatValue = that.get();
return value.equals(thatValue);
}
@Override
public int hashCode() { return 37 * value.hashCode(); }
}
view raw Some.java hosted with ❤ by GitHub
Como Some representa a existencia de valor o método hasValue() sempre retorna true, o método get() o valor.

Agora o subtipo que representa ausência de valor:

public final class None<T> extends Option<T> {
public static class NoneHasNoValue extends RuntimeException {}
public None() {}
public boolean hasValue() { return false; }
public T get() { throw new NoneHasNoValue(); }
@Override
public String toString() { return "None"; }
@Override
public boolean equals(Object other) {
return (other == null || other.getClass() != None.class) ? false : true;
}
@Override
public int hashCode() { return -1; }
}
view raw None.java hosted with ❤ by GitHub
Como None representa ausência de valor o método hasValue() sempre retornará falso, e lançaremos uma exceção caso seja tentado recuperar o valor que não existe. Já que None não possui valor, o método equals() sempre retorna true caso seja passada outra instância de None.

Agora vamos fazer uso do nosso novo sistema de tipos:

@Before
public void setup() {
names = new ArrayList<Option<String>>();
names.add(new Some<String>("Renan"));
names.add(new None<String>());
names.add(new Some<String>("Paulo"));
}
@Test
public void usandoGetOrElse() {
String[] expected = { "Renan", "Valor alternativo!", "Paulo"};
System.out.println("*** Usando getOrElse:");
for (int i = 0; i < names.size(); i++) {
Option<String> option = names.get(i);
String value = option.getOrElse("Valor alternativo!");
System.out.println(option + " = " + value);
assertEquals(expected[i], option);
}
}
// Saída:
// Some(Renan) = Renan
// None(Valor alternativo) = Valor alternativo
// Some(Paulo) = Paulo
view raw SomeENone.java hosted with ❤ by GitHub
No código acima criamos 3 Options e iteramos sobre eles chamando o método getOrElse(T alternative), dessa forma, se existe um valor, o mesmo é retornado, caso contrário é retornado o valor alternativo.

Também podemos voltar á abordagem mais clássica, perguntando se existe valor no Option:

@Test
public void verificaSePossuiValor() {
String[] expected = { "Renan", null, "Paulo"};
for (int i = 0; i < names.size(); i++) {
Option<String> option = names.get(i);
if (option.hasValue()) {
String value = option.get();
System.out.println(option + " = " + value);
assertEquals(expected[i], value);
}
}
}
Chamamos o método hasValue para verificar se aquela Option possui um valor e então o usamos.

Por fim, vamos ver como fica o código dos nossos métodos que podem retornar um valor ou ausência de valor e seu uso:

public Option<String> fazAlgo() {
if(se tenho valor retornarei um Some com o valor) {
return new Some<String>(valor);
} else {
// não tenho valor, ao invés de retornar nulo
//e correr o risco do código cliente chamar um método em null, retornarei um None
return new None<String>();
}
}
// Option<String> option = fazAlgo();
Note que o grande ganho com essa abordagem é deixar explicito através de código que o valor que estamos usando é opcional, o retorno do método agora é um Option, bem mais legível e menos passível de erros, pois sendo um Option, devemos determinar se o Option é uma instância de Some antes de usar o valor.

Desenvolvendo nossas próprias classes para melhorar o sistema de tipos nesse caso, parece overhead, uma solução é o uso do Guava, uma lib que possui recursos interessantes que dão um "sabor"funcional para nossos projetos.

Aqui tem uma rápida introdução de como usar esses recursos com o Guava.

segunda-feira, 12 de agosto de 2013

Por que preciso declarar como final variáveis passadas à classes anônimas (inner class)?

É muito comum quando estamos desenvolvendo interfaces gráficas seja em Swing ou Android o uso de classes anônimas, geralmente usamos classes anônimas como callbacks em listeners de eventos. Devido às características do Java, quando precisamos de um callback passamos uma instância de classe como parâmetro ao invés de simples funções como podemos fazer em Javascript por exemplo.

public void criaInterface() {
String var = "content";
botao.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
System.out.println(var); // ERRO!
}
});
}
view raw innerClassFail hosted with ❤ by GitHub
Repare que no código acima temos um erro, pois tentamos acessar a variável "content" dentro de uma inner class. Para resolver o problema é simples, basta declararmos "content" como final.

public void criaInterface() {
final String var = "content";
botao.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
System.out.println(var); // OK!
}
});
}
view raw innerClassOk hosted with ❤ by GitHub
Para entender porque devemos declarar variáveis a serem acessadas em inner classes como final, devemos primeiro entender como inner classes são traduzidas em byte code.

Quando temos uma inner class ela não é gerada no mesmo arquivo de byte code da classe que a contém (outer class), são gerados dois arquivos, um com a outer class e outro com a inner class:

Classe1.class
Classe1$OnClickListener.class

Aqui surge um problema, nossa classe Classe1$OnClickListener.class precisa acessar a variável "content" declarada em Classe1.class. Para que isso seja possível o compilador cria em Classe1$OnClickListener.class um atributo que recebe uma cópia do valor da variável da outer class. Mas imagine que por algum motivo a variável em Classe1.class ou mesmo o atributo em Classe1$OnClickListener.class tenha seu valor alterado, teriamos um problema de sincronização de dados. Para evitar este problema somos obrigados a declararmos as variáveis que serão usadas em inner classes como final, assim os valores tanto nas outer classes e inner classes serão iguais.

Referências:

  • http://stackoverflow.com/questions/7423028/java-local-variable-visibility-in-anonymous-inner-classes-why-is-final-keywo
  • http://techtracer.com/2008/04/14/mystery-of-accessibility-in-local-inner-classes/


sexta-feira, 5 de abril de 2013

Scala para programadores Java

Você é um programador Java experiente, está sempre buscando conhecimento e novas formas de encontrar soluções. Uma forma de atingir este objetivo é aprender uma nova linguagem, preferencialmente com um paradigma diferente. Scala te chama atenção, por funcionar dentro da JVM, usar classes existentes do Java e por ser funcional, um paradigma que você ainda não conhece.

Então caro leitor temos muito em comum, na saga de estudar a linguagem e também o paradigma funcional, tive a oportunidade de receber a ajuda de um hacker amigo, @JonasAbreu. O coach do Jonas está sendo fundamental. Fui à minha primeira reunião dos ScalaDores, o grupo é bem avançado, portanto se você conhece bem Scala será um prato cheio, se está iniciando, também não tem problema, você acaba tendo um bom aproveitamento.

Uma grande questão é que você não gostaria de reaprender tudo que já sabe sobre programação, orientação a objetos e etc. Seria interessante se tivesse uma forma de aprender reaproveitando sua experiência, focando apenas nas novidades e de forma bem prática.

Pensando nisso, começo a partir deste post, fazer uma série para programadores Java interessados em aprender Scala. Abordarei tudo de forma muito prática e bem baseada em coisas que acho importante ou código que usei para alguma solução, MUITO DO CÓDIGO SERÁ AUTO EXPLICATIVO. Explicarei a teoria e detalhes quando o código não for tão obvio assim.

Para começar sugiro usar o interpretador de Scala no terminal. Instalar, iniciar o interpretador, tudo isso é bem fácil e já existe bons materiais, portanto não serão abordados aqui, ao menos nesse primeiro momento.

Vamos começar!

Variáveis
Para declarar uma variável basta fazer:

var nomeDaVariavel = valor

var nome = "Renan"
view raw gistfile1.scala hosted with ❤ by GitHub

Note que diferente do Java não precisamos declarar o tipo da variável, isso porquê Scala consegue, muitas vezes, inferir o tipo declarado. Mas caso quisermos declarar o tipo, por questões de legibilidade por exemplo, podemos fazer:

var nomeDaVariavel : TipoDaVariavel = valor

var nome : String = "Renan"
//ou com nome qualificado do tipo
var nome : java.lang.String = "Renan"
view raw gistfile1.scala hosted with ❤ by GitHub
Também podemos criar uma variável "final" como em Java, para isso declaramos:

val idade = 26
//ou declarando o tipo
val idadeFinal : Int = 26
// ou ainda declarando o tipo com nome qualificado
val idadeFinal : scala.Int = 26
view raw gistfile1.scala hosted with ❤ by GitHub
Assim se você tentar atribuir um novo valor à variável idadeFinal receberá um erro:

error: reassignment to val

A variável "nome" é do tipo String, Scala não tem uma classe String então ela usa classe java.lang.String do Java. Já "idade" é do tipo Int (scala.Int) que é correspondente ao tipo primitivo int, que temos no Java. Scala tem tipos correspondentes para os tipos primitivos do Java, por exemplo scala.Float para float, scala.Char para char. Quando seu código é compilado em bytecodes o compilador tenta usar os tipos primitivos do Java por questões de performance.

Funções
Funções requerem uma atenção especial, pois podemos encontrar a mesma função declarada de várias formas, o que pode assustar de início, mas depois que aprendemos fica bem fácil.

Para declarar uma função:
def funcao(param : TipoParam) : TipoRetorno = {
  // corpo
}


Uma função que soma dois números:

def soma(numero1 : Int, numero2 : Int) : Int = {
numero1 + numero2
}
view raw gistfile1.scala hosted with ❤ by GitHub
Temos sempre de declarar o tipo dos parâmetros, a função recebe dois parâmetros do tipo Int e retorna também um tipo Int. Note que após a declaração do tipo de retorno vem um sinal de igualdade "=" e então as chaves "{}" que é onde deve ficar o corpo do método.

Em alguns casos* Scala consegue inferir o tipo do retorno da função, como é o nosso caso. Então podemos declarar a mesma função omitindo o tipo de retorno:

def soma(numero1 : Int, numero2 : Int) = {
numero1 + numero2
}
view raw gistfile1.scala hosted with ❤ by GitHub
Como nossa função possui apenas uma linha podemos também omitir as chaves:

def soma(numero1 : Int, numero2 : Int) = numero1+ numero2
view raw gistfile1.scala hosted with ❤ by GitHub
* Se nossa função for recursiva temos de explicitar o tipo de retorno.

Podemos declarar funções sem retorno também:
def imprimeTexto() : Unit = println("Apenas um texto aqui")
view raw gistfile1.scala hosted with ❤ by GitHub

Neste caso Unit é o equivalente ao void do Java. Você pode omitir o retorno neste caso também:
def imprimeTexto() = println("Apenas um texto aqui")
view raw gistfile1.scala hosted with ❤ by GitHub

Um pouquinho de características funcionais
Uma das principais características de linguagens funcionais é poder passar funções como parâmetros. Vamos a um exemplo bem prático disso, vamos criar uma classe Array e forneceremos uma maneira de executar um "processamento" em cada elemento do array.

public class Array<T> {
List<T> elementos = new ArrayList<>();
public void foreach_(Funcao<T> f) {
// para cada elemento chamamos o método executaEm da interface Funcao
for(T e : elementos) {
f.executaEm(e);
}
}
public void add(T t) {
elementos.add(t);
}
// outros métodos de array
}
view raw gistfile1.java hosted with ❤ by GitHub
Para isso precisamos definir nossa interface Funcao<T> que possui o método executaEm:

public interface Funcao<T> {
public void executaEm(T e);
}
view raw gistfile1.java hosted with ❤ by GitHub

Agora podemos usar nosso Array. Vamos supor que temos um Array de String, com nomes de pessoas e queremos apenas imprimir o nome das pessoas em maiúsculo.

public static void main(String[] args) {
Array<String> nomes = new Array<>();
array.add("Renan");
array.add("José");
nomes.foreach_(new Funcao<String>() {
@Override
public void executaEm(String e) {
System.out.println(e.toUpperCase()); // ou qualquer código aqui
}
});
}
view raw gistfile1.java hosted with ❤ by GitHub
Ficou bacana. Mas repare que precisamos de uma interface classe Funcao<T> que possui apenas um método. Depois precisamos de uma implementar essa interface (no nosso caso por uma classe anônima), tudo para executar o processamento do método, podemos resumir tudo isso. Em Scala temos o recurso de funções anônimas ou "function literal" que tornam as coisas bem interessantes, o mesmo código acima pode ser escrito como:

nomes.foreach(nome => println(nome)) // existem outras formas de escrever essa linha, abordaremos futuramente
view raw gistfile1.scala hosted with ❤ by GitHub
nomes.foreach(nome => println(nome)) // existem outras formas de escrever essa linha, abordaremos futuramente
Imagine quantas APIs poderiam se beneficiar dessa abordagem. Lembre-se da API do AWT/Swing. Era muito improdutivo desenvolver listeners para nossos elementos de tela (botões por exemplo). Ao invés de escrever uma classe anônima, implementar um método e etc, se reproduzirmos a API em uma linguagem funcional,  podemos apenas passar uma função anônima como citado anteriormente.

//recebendo uma function literal
var button = new Button;
button.addActionListener((e) => println("codigo do listener"))
view raw gistfile1.scala hosted with ❤ by GitHub
//api em java
Button b = new Button();
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// código do botão
}
});
view raw gistfile2.java hosted with ❤ by GitHub

Não se preocupe com relação ao código acima, voltaremos à essas características futuramente.