Tipos de dados em Groovy

Neste artigo vamos analisar os tipos de dados que estão disponíveis na linguagem de programação Groovy. Veremos na prática como alguns tipos da plataforma Java influenciaram a linguagem e são até mesmo usados no Groovy.

O Groovy foi criado por volta de 2003 como uma linguagem dinâmica e com o objetivo de rodar na JVM, ou seja, compilada diretamente para bytecodes, com a elegância de linguagens como Python e Ruby e ao mesmo tempo com a capacidade de interagir com código Java já existente. Assim, ele conta com vários recursos e sintaxe mais simples que a do Java, mesmo sofrendo muita influência dos tipos de dados disponíveis em Java, como veremos isso no decorrer do artigo.

Para acompanhar os exemplos que serão apresentados neste artigo é necessário que o leitor possua a linguagem Groovy instalada em sua máquina. Caso ainda não tenha, o processo de instalação é simples: apenas baixe o zip no site da linguagem e descompacte-o em um diretório de sua preferência. Além disso, será necessário configurar as variáveis de ambiente GROOVY_HOME e PATH para que os comandos da linguagem sejam encontrados em um terminal ou prompt de comando.

A variável GROOVY_HOME deve conter como valor o caminho para a pasta onde o Groovy foi descompactado:

GROOVY_HOME=c:\groovy-2.4.5

E a variável PATH deve conter o caminho para a pasta bin que está localizada dentro do diretório do Groovy:

PATH=JAVA_HOME/bin;GROOVY_HOME/bin

Para testar a instalação basta digitar no terminal ou prompt o seguinte comando:

$ groovy --version

Ele deve ter como resposta a versão da linguagem instalada:

=>Groovy Version: 2.4.5 JVM: 1.8.0_74 Vendor: Oracle Corporation OS: Linux

Junto com a linguagem temos o Groovy Console, que vai ajudar na execução dos códigos. Conheceremos mais dela na seção a seguir.

Groovy Console

Essa ferramenta é usada para compilar, executar e mostrar o resultado da execução de códigos Groovy. Para executá-la basta digitar no console o comando groovyConsole e como resultado uma nova janela será mostrada, conforme a Figura 1. Perceba que ela contém um campo semelhante a um editor de texto e logo abaixo um display para apresentar as saídas do código executado

Figura 1. Groovy console.

A execução do código pode ser feita de duas maneiras: pressionando o botãoou por meio do atalho Ctrl + R. Para limpar o display de resultados deve-se acessar o menu view → Clear Output, ou apenas usar o atalho Ctrl + W.

Tipos primitivos

Variáveis de tipos primitivos são aquelas que guardam informações mais simples ou básicas, além de fazer chamadas aos métodos. Não existem tipos primitivos em Groovy, ou seja, todas as variáveis são objetos, até mesmo valores literais escritos diretamente no código como, por exemplo, true, false, 10. Isso pode ser comprovado por meio da ferramenta Groovy Console através do teste com o código da Listagem 1.

Listagem 1. Código chamando métodos em variáveis aparentemente primitivas.

println 10.class println 100.5.class println true.class println false.class

Perceba que estamos acessando uma propriedade a partir de um valor que parece um primitivo. Isso pode parecer estranho, especialmente para programadores Java, mas lembre-se que esses valores são na verdade referências a objetos. A seguir vemos a saída de execução deste código:

=> class java.lang.Integer => class java.math.BigDecimal => class java.lang.Boolean => class java.lang.Boolean

Valores literais

São aqueles que estão explicitamente dispostos no código, como os apresentados na Listagem 1, que não foram resultado de uma operação ou informados durante a execução do programa. Existem tipos pré-definidos para valores literais e na Tabela 1 vemos alguns exemplos de valores literais com seus respectivos tipos.

Classe

Valor literal de exemplo

java.lang.Long

1000L

java.lang.Integer

100

java.lang.Float

1.0F

java.lang.Double

100D

java.math.BigDeciamal

1000.50

java.math.BigInteger

123G

Tabela 1. Tipos pré-definidos

Note que para obter variáveis do tipo long, flot, double e big integer é necessário informar isso passando os sinais “L”, “F”, “D” e “G”, respectivamente, já que estes não são os tipos padrões para os valores literais.

Tipagem dinâmica e estática

Em Groovy, especificar o tipo de uma variável é opcional: caso o tipo não seja explicitamente informado, a linguagem vai inferir o seu tipo, baseando-se nas caracterizas do valor passado. Isso pode ser observado na Listagem 5, onde vemos um método avaliarTipo que testa o tipo da variável passada.

Listagem 5. Código para teste de tipo da variável.

idade = 20 avaliarTipo(idade) string = "Hello!" avaliarTipo(string) populacao = 100000000L avaliarTipo(populacao) orcamento = 200.00d avaliarTipo(orcamento) preco = 200.00 avaliarTipo(preco) def avaliarTipo(variavel){ switch(variavel.getClass()){ case Integer: println("A variável foi avaliada como Inteiro") break case BigDecimal: println("A variável foi avaiada como BigDecimal") break case Double: println("A variável foi avaliada como Double") break case String: println("A variável foi avaliada como String") break case Long: println("A variável foi avaliada como Long") break case BigInteger: println("A variável foi avaliada como BigInteger") break } }

A saída de execução desse código é vista a seguir, onde para cada chamado do método avaliarTipo uma linha foi impressa informado o tipo da variável passada.

=> A variável foi avaliada como Inteiro => A variável foi avaliada como String => A variável foi avaliada como Long => A variável foi avaliada como Double => A variável foi avaiada como BigDecimal

Além da tipagem dinâmica, o Groovy também permite ao programador especificar explicitamente os tipos das variáveis, passando o tipo antes do seu nome, como mostra a Listagem 6.

Listagem 6. Definição de variáveis explicitamente tipadas.

Integer idade = 10 Double valor = 1.0d String nome = “José Camilo”

A vantagem nessa tipagem é que o compilador não necessitará executar procedimentos para definir o tipo da variável, logo essa estratégia de código é ligeiramente mais rápida.

Outro ponto a destacar é que, uma vez definidos, os tipos não podem ser alterados, porque uma variável do tipo string nunca poderá armazenar um valor numérico e vice-versa, como mostra o exemplo da Listagem 7.

Listagem 7. O tipo de uma variável Groovy não pode ser alterado após a sua definição.

Integer preco = 1000 println preco.class preco = "Mil Reais"

O código defini uma varável preco do tipo inteiro e atribui a ela o valor 1000; depois de imprimir o seu tipo, o código tenta atribuir um valor do tipo string e então uma exceção é lançada, como mostra a saída a seguir:

=> class java.lang.Integer => Exception thrown => org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'Mil Reais' with class 'java.lang.String' to class 'java.lang.Integer' at ConsoleScript34.run(ConsoleScript34:5)

String Ranges

Ranges são intervalos de valores, ou seja, objetos que armazenam valores específicos. Por exemplo: um range pode armazenar um valor de 1 a 10, ou até mesmo de caracteres como de a até z. Esse tipo pode ser definido de duas formas, como mostra a Listagem 8. A variável a define um range que inclui o último elemento informado; para o range armazenado em b o último elemento não é incluído: isso é definido por meio do sinal <.

Listagem 8. Sintaxe de declaração para ranges.

a = [primeiro_elemento] . . [ultimo_elemento_incluso] b = [primeiro_elemento] . . < [ultimo_elemento_nao_incluso]

Para percorrer os elementos de um range é possível usar o loop for ou até mesmo o método each disponível para objetos do tipo range, como mostra a Listagem 9. Veja o uso dessas estruturas para percorrer os itens de três objetos range's> perceba que um deles é um intervalo de letras e não de números.

Listagem 9. Código de exemplo para percorrer range'

println("INTERVALO1") intervalo1 = 1..15 for(int i = 0; i < intervalo1.size(); i++){ print("${intervalo1[i]} ") } println("INTERVALO2") intervalo2 = 15..<30 for(item in intervalo2){ print("$ ") } println("ALFABETO") alfabeto = 'a'..'z' alfabeto.each{letra -> print("$ ")}

O range intervalo1 armazena os valores de 1 até 15, e ele foi percorrido usando um for clássico ou tradicional, idêntico ao do Java. O intervalo2 foi percorrido usando um loop for simplificado, que apenas recebe cada elemento do range em uma determinada variável, no caso, a variável item. O range que armazena as letras de 'a' até 'z' foi percorrido utilizando o método each, que recebe um bloco de código para manipular o item em iteração. A saída do código é exibida a seguir:

=> INTERVALO1 => 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 => INTERVALO2 => 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 => ALFABETO => a b c d e f g h i j k l m n o p q r s t u v w x y z

Além de letras, como mostrado no último exemplo, é possível também definir intervalos de datas, como mostra a Listagem 10.

Listagem 10. Exemplo de objeto range armazenando intervalo de datas.

data1 = new Date().parse("dd/MM/yyyy", "01/01/2016") data2 = new Date().parse("dd/MM/yyyy", "10/01/2016") intervalo = data1..data2 intervalo.each{data -> println(data.format('dd/MM/yyyy'))}

Esse código utiliza o método each para percorrer o intervalo de datas e imprimir cada uma delas. A saída deste código é apresentada a seguir:

=> 01/01/2016 => 02/01/2016 => 03/01/2016 => 04/01/2016 => 05/01/2016 => 06/01/2016 => 07/01/2016 => 08/01/2016 => 09/01/2016 => 10/01/2016

Range's também podem ser usados nas instruções case do bloco condicional switch. Se o valor passado estiver dentro do intervalo definido pelo range o bloco de código associado é executado, como mostra a Listagem 11.

Listagem 11. Exemplo de uso de range's em instruções switch.

def definirIdade(idade){ switch(idade){ case 0..10: println("Você tem entre 0 e 10 anos.") break case 11..17: println("Você tem entre 11 e 17 anos.") break case idade >= 18: println("Você tem 18 anos ou mais."); break } }

Os range's também dispões de vários métodos utilitários que podem ajudar na manipulação das informações. Veja na Listagem 12 o uso dos métodos contains, size e as propriedades to e from.

Listagem 12. Exemplo de uso dos métodos contains e size e propriedades from e to.

intervalo1 = 'a'..'e' if(intervalo1.contains('d')){ println('O intervalo contém a letra d') } println "O intervalo tem ${intervalo1.size()} elementos" println "O primeiro elemento é '${intervalo1.from}'" println "O último elemento é '${intervalo1.to}'"

O método contains verifica se o parâmetro passado está contido no intervalo e retorna true ou false. O método size retorna a quantidade de elementos encontrados no intervalo. Já as propriedades from e to retornar, respectivamente, o primeiro e o último elemento do range. A saída do código é mostrada a seguir:

=> O intervalo contém a letra d => O intervalo tem 5 elementos => O primeiro elemento é 'a' => O último elemento é 'e'

Listas

As listas do Groovy aproveitam o que há de melhor nos arrays do Java, bem como na API de collection, como as classes ArrayList e LinkedList. Para criar uma lista em Groovy basta seguir a sintaxe:

lista = [item1, item2, item3, item4, itemN]

Por padrão, essas listas são instâncias da classe java.util.ArrayList . Veja na Listagem 14 o código que imprime o tipo de uma lista.

Listagem 14. Instanciando e imprimindo o tipo de uma lista.

nomes = ["jose", "joao", "silva", "souza"] println nomes.class

O código exibe o seguinte resultado:

=> class java.util.ArrayList

É possível especificar o tipo de lista que se deseja criar no momento do processo de instância. Veja a seguir um exemplo de código criando uma lista do tipo LinkedList:

lista = new LinkedList(); println lista.class

Esse código imprime como resultado o tipo da lista criada, como mostrado a seguir:

=> class java.util.LinkedList

Além disso, também é possível criar uma lista a partir de um range, como exibido no código a seguir:

range = 1..10 List lista = range.toList()

Operações com Listas

O método addAll permite adicionar todos os itens de uma lista em outra, como mostra o exemplo da Listagem 15.

Listagem 15. Exemplo de uso do método addAll.

nomes1 = ['souza','silva','carlos','joao'] println nomes1.size() //4 nomes2 = ['camilo','machado','mateus','paulo'] nomes1.addAll(nomes2) println nomes1.size() //8

Para acessar elementos de uma lista pode ser usado o índice do elemento entre colchetes “[]”, bem como passar o índice como parâmetro para o método get. Veja um exemplo na Listagem 16.

Listagem 16. Acessando elementos de uma lista.

precos1 = [10, 20, 30.0, 40] println "Elemento da posição 0: ${precos1[0]}" println "Elemento da posição 3: ${precos1.get(3)}"

Esse código apresenta na saída o resultado a seguir; perceba que os índices ou posições das listas iniciam em 0:

=> Elemento da posição 0: 10 => Elemento da posição 3: 40

Também é possível criar uma lista que contenha outras listas como itens, que pode representar uma matriz ou lista multidimensional:

lista = [[1,2,3],[4,5,6],[7,8,9]]

Assim como nos range's, o método size() pode ser usado para recuperar o tamanho de uma lista. Com os métodos get e size já analisados é possível percorrer uma lista, como mostra o exemplo da Listagem 17.

Listagem 17. Uso dos métodos get e size

lista = [1,2,3,4,5,6,7,8,9] for(int i = 0; i < lista.size();i++){ print lista.get(i) }

Também é possível acessar os dados de uma lista passando como parâmetro um range, como mostra a Listagem 19.

Listagem 19. Acessando itens de uma lista por um range.

clientes = ['joao', 'silva', 'oliveira', 'neto', 'pereira', 'manoel'] intervalo = 2..5 println clientes[intervalo]

Neste caso apenas os itens com índices entre 2 e 5 vão ser impressos no display de saída, como mostra o bloco a seguir:

=> [neto, pereira, manoel]

Ainda é possível coletar itens específicos de listas. Isso é feito passando os índices dos itens desejados separados por vírgula, como mostra o código a seguir:

clientes = ['joao', 'silva', 'oliveira', 'neto', 'pereira', 'manoel', 'carlos'] println clientes[0,2,5,6]

Já no caso da execução deste código apenas os itens com os índices 0, 2, 5 e 6 são impressos:

=> [joao, oliveira, manoel, carlos]

Usando essa mesma estratégia é possível remover elementos de uma lista, como mostra a Listagem 20.

Listagem 20. Exemplo de remoção de itens com ragen's.

clientes = ['joao', 'silva', 'oliveira', 'neto', 'pereira', 'manoel', 'carlos'] clientes[0..2] = [] println clientes

Esse código imprimirá a lista apresentada a seguir; note que os índices de 0 até 2 foram removidos:

=> [neto, pereira, manoel, carlos]

É possível adicionar e remover itens em listas de várias maneiras. Na Listagem 21 vemos alguns exemplos de código adicionando e removendo elementos em listas.

Listagem 21. Exemplo de código para adicionar e remover itens de listas.

clientes = [] clientes += 'jose' clientes += ['elton', 'maria', 'lucena'] clientes << 'carvalho' clientes -= 'jose' println clientes

Note que foram usados os operadores += e << para adicionar elementos e o operador -= para exclui-los da lista. Ao ser executado o código anterior gera a seguinte saída:

=> [elton, maria, lucena, carvalho]

Mapas

São estruturas bastante semelhantes as listas: a diferença principal entre essas duas estruturas é que os elementos de um mapa não acessíveis por um índice, mas sim por uma chave, que pode ser qualquer objeto. A sintaxe para a declaração de um map está descrita a seguir:

map = [chave1: valor1, chave2: valor2, chave3: valor3, chaveN: valorN]

Para adicionar ou remover elementos em um map pode-se usar a notação descrita na Listagem 22.

Listagem 22. Exemplo de código para adicionar ou remover elementos de um map.

map["nome"] = "daniel" map["sobrenome"] = "tavares" map["email"] = "jdanieltrs1@gmail.com" map["endereco"] = "Rua do Construtor José Sabino" map["endereco"] = null println map

O código usou a notação “[chave] =” para configurar um valor para determinada chave. Para remover determinado valor uma atribuição com “null” foi executada.

Uma das maneiras mais fáceis de percorrer ou ler dados de um map é utilizar o método each que funciona de uma forma um pouco de diferente desse mesmo métodos para listas. Veja na Listagem 23 o funcionamento deste método.

Listagem 23. Usando o método each em um map.

map["nome"] = "daniel" map["sobrenome"] = "tavares" map["email"] = "jdanieltrs1@gmail.com" map["endereco"] = "Rua do Construtor José Sabino" map.each{ entrada -> println "${entrada.key} - ${entrada.value}" }

Perceba que no método each usamos as propriedades key e value para recuperar a chave e o valor de cada item do map. A saída desse código é exibida a seguir.

=> nome - daniel => sobrenome - tavares => email - jdanieltrs1@gmail.com => endereco - Rua do Construtor José Sabino

Essa foi uma pequena introdução aos principais tipos de dados da linguagem Groovy e suas principais características. Saber destes pontos é de suma importância antes de entrar em detalhes mais profundos sobre a linguagem. Assim, vemos que a manipulação de informações nas estruturas mais usadas no Groovy é algo relativamente simples, e mais ainda para quem já é desenvolvedor Java, pois muito dessa se aproveita dentro do Groovy.

Referências

Documentação Groovy
http://www.groovy-lang.org/documentation.html

Artigos relacionados