Groovy vs Java: listas

O erro mais comum de quem está aprendendo Grails é programar em Groovy exatamente como seria feito em Java. É um comportamento comum, visto que aparentemente não há tantas diferenças sintáticas assim entre as duas linguagens. Reparou que coloquei “aparentemente” em negrito?

Este post é o primeiro de uma série na qual pretendo comparar as duas linguagens e tentando convencê-los a não cometer o pecado de se programar em Groovy como se fosse Java.

Primeira diferença: as listas estão presentes na sintaxe do Groovy

Em Java  para criar uma lista  o caminho padrão é instanciar alguma classe que implemente a interface java.util.List, gerando código como o abaixo:


List<Tipo> lista = new ArrayList<Tipo>();

Em Groovy temos um elemento sintático só para lidar com listas que são os colchetes. O mesmo código que escrevi em Java, eu reescreveria em Groovy assim:


def lista = []

A variável lista armazena agora uma instância de java.util.ArrayList. Repare: eu não preciso definir (e nem tenho como usando esta sintaxe) o tipo que esta lista irá armazenar, porque Groovy possui tipagem dinâmica.

E sabe o que é mais legal? Eu também posso já instanciar minha lista preenchida, tal como nos exemplos abaixo:


def lista_de_strings = ["Uma bonita lista", "com muitas", "strings"]

def lista_ecletica = ["Uma string", 23, true, false, ["uma", "lista", "interna"]]

Em Java é possível instanciar uma lista e ao mesmo tempo já definir o seu conteúdo. Há duas maneiras de se fazer isto: você pode usar um construtor que receba como parâmetro um objeto que implemente a interface java.util.Collection OU pode usar um código bem feio usando inner classes. Quer ver? Bem, aqui vai:


List<String> lista_strings = new ArrayList<String>() {{
add("Uma lista");
add("Com código muito");
add("Feio");

}};

ALERTA: se estiver programando em Groovy exatamente como faz em Java, é certo que ao topar com uma lista como as que expus acima você irá acreditar se tratar de um array.

Acessando elementos

Como em Java as listas são implementações da interface java.util.List, acessar os elementos sempre é feito pelo método get, como no exemplo abaixo:


String valor = lista.get(1); // pego o segundo elemento

Sabe, em Groovy eu posso fazer exatamente como em Java, já que é uma instância de java.util.ArrayList. E também de uma forma mais bacaninha. :)


def valor = lista[1] // pego o segundo elemento

def valor2 = lista.get(1) // a la Java

Inserindo e removendo elementos

Em Java, quando queremos adicionar um elemento em uma lista, usamos o método add, como no código abaixo:


lista.add("String quente");

Em Groovy podemos usar um recurso poderoso que é a sobrescrita de operadores. Quer ver como funciona?


def lista = []

lista += "Uma string" // O operador += serve para inserir um elemento na lista

lista.add("Uma outra string") // a la Java

def nova_lista = lista + "Adendo" // crio uma nova lista, chamada nova_lista, que contém o conteudo de lista + uma String

Resumindo: o operador += inclui elementos na lista, enquanto o operador + cria uma nova lista.

E pra remover você provávelmente já sabe também:


def lista = ["um", "Monte", "de", "strings"]

lista -= "Monte" // removo o elemento "Monte"

def nova_lista = lista - "de" // crio uma nova lista que não contém o elemento "de"

Outro resumo: o operador – remove um elemento, enquanto o operador -= cria uma nova lista que não contém o elemento passado como parâmetro.

Buscando elementos

Se em Java você quer retornar todos os elementos de uma lista que satisfaçam a um critério, o código a ser escrito vai ser parecido com isto:


List<Pessoa> pessoas = new ArrayList<Pessoa>() {{

add(new Pessoa("Kico", 32);
add(new Pessoa("Loló", 29);
add(new Pessoa("Theo", 0);

}};

List<Pessoa> pessoas_com_mais_de_um_ano = new ArrayList<Pessoa>();

for (Pessoa pessoa : pessoas) {

if (pessoa.getIdade() > 1) pessoas_com_mais_de_um_ano.add(pessoa);

}

E em Groovy, como faço hein? Assim:


def pessoas = [new Pessoa([nome:"Kico", idade:32]), new Pessoa([nome:"Loló", idade:29]),
new Pessoa([nome:"Theo", idade:0])]

def resultado = pessoas.findAll { it.idade > 1 }

Eu uso a função findAll, que recebe como parâmetro uma closure. O papel desta é simples: retornou true? Adicione na lista de retorno.

E se eu quiser só o primeiro resultado?


def resultado = pessoas.find {it.idade > 1}

Vou retornar não uma lista, mas a primeira instância que satisfaça ao resultado.

Outro problema: como você verifica se pelo menos um objeto em uma lista satisfaça a uma condição? Em Java, escrevemos um monstrinho como o código abaixo:


public boolean existeUmBebe(List<Pessoa> lista) {

for (Pessoa pessoa : lista) {

if (pessoa.getIdade() < 1) return true;

}

return false;

}

Wow! E como eu faço em Groovy hein? Eu uso a função any


pessoas.any { it.idade < 1 }

A função any, assim como find e findAll são injetadas em todas as listas pelo Groovy, e todas recebem como parâmetro a closure que verifica a condição.

Iterando sobre o conteúdo

Em ambas as linguagens, iteramos sobre o conteúdo usando o loop for. Até ai, nada demais. A diferença é que Groovy injeta em nossas classes um método chamado each, que recebe como parâmetro uma closure. Então é possível iterar em cima do resultado de uma consulta de forma bastante simples, tal como no exemplo abaixo:


def lista = [new Pessoa([nome:"Kico", idade:32]), new Pessoa([nome:"Loló", idade:29]),
new Pessoa([nome:"Theo", idade:0])]

lista.findAll { it.idade > 0 }.each {
println it.nome
}

O parâmetro que a closure recebe é o elemento corrente da iteração.  E sabe o que é mais legal? A closure não precisa ser passada na hora.


def trabalhe = {pessoa ->

if (pessoa.idade > 0) { println "Tá velho!"} else {println "Ta quase velho!"}

}

pessoas.each trabalhe

Coletando valores de atributos

Imagine que no nosso exemplo, precisemos coletar todos os nomes das pessoas presentes em nossa lista. Em Java, escreveríamos algo como o código abaixo:


List<String> nomes = new ArrayList<String>();

for (Pessoa pessoa : pessoas) { nomes.add(pessoa.getNome()); }

Em Groovy, usamos a função collect que é injetada nas coleções. Esta recebe como parâmetro uma closure que será iterada em cima de todos os elementos da lista. O valor de retorno de cada iteração é adicionado a uma lista que é o resutlado da função. É complicado de descrever, mas vendo o código fica mais fácil. :)


def nomes = pessoas.collect { it.nome }

Concluindo

Eu só arranhei a superfície do assunto. O que fica claro é: com Groovy você escreve muito menos código.  Um ponto importante a ser salientado é: quando escrevemos código em Groovy usando a sintaxe do Java estamos dificultando a vida do compilador, que sofrerá horrores tentando otimizar o seu código. Há portanto duas consequências neste pecado:

  • Digita-se bem mais
  • O código é mais lento

Pra finalizar, dois links que ajudam muito quem está aprendendo:

Groovy Console: http://groovyconsole.appspot.com/ (pra você experimentar código Groovy online)

Coleções em Groovy: http://groovy.codehaus.org/JN1015-Collections (diversos dos detalhes que não citei estão aqui)

9 comentários em “Groovy vs Java: listas”

  1. Devo ter menos de seis meses que conheço Groovy, mas nesse pouco tempo já pude me apaixonar pelas facilidades do Groovy. Depois que se aprende um pouco você consegue fazer coisas altamente criativas e com pouquíssimo código.

    E realmente, a parte de Collections é a que acho mais legal.

    Vou deixar um blog que me deu altas dicas para pensar um pouco fora da caixa (Java, rs).

    Groovy Goodness: http://mrhaki.blogspot.com/search/label/Groovy%3AGoodness

  2. É possivel fazer uma consulta utilizando o like para uma String? Algo como lista.findAllByTitleLike(“%roberto%”)

    1. Kico (Henrique Lobo Weissmann)

      Leandro, é sim. Você escreveria algo similar ao código abaixo:

      lista.findAll {
      it.contains(“texto qualquer”)
      }

  3. Obrigada pelo artigo! Me ajudou demais, comecei a utilizar o groovy a duas semanas, e estou gostando muito mas também sofrendo um pouco com a diferença em relação ao Java.

    1. Kico (Henrique Lobo Weissmann)

      Oi Juliana, eu que agradeço!
      Pode doer um pouco especialmente em versões mais antigas do Groovy no que diz respeito ao uso de Lambdas, que não era tão parecido assim e a sintaxe podia ser um pouquinho diferente.
      Mas agora tá pra sair o Groovy 3 com uma sintaxe bem mais próxima disto aí.
      Qualquer coisa, to aqui pra ajudar, desculpe a demora na minha resposta!

Deixe uma resposta

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.

Rolar para cima