Esse artigo faz parte da revista Java Magazine edição 32. Clique aqui para ler todos os artigos desta edição

ht=34 alt=imagem_pdf.jpg src="/imagens/imagem_pdf.jpg" width=34 border=0>

Groovy: Java Através de Scripts

Desenvolvimento Ultra-rápido e Versátil sobre a JVM

Detalhes sobre o Groovy, a nova linguagem dinâmica para a plataforma Java, e uma introdução às tecnologias e conceitos relacionados

Osvaldo Pinali Doederlein

Em diversos artigos da Java Magazine, enfatizamos a distinção entre a Plataforma Java e a Linguagem Java. Java, a Linguagem é essencialmente uma sintaxe particular na qual podemos escrever programas que executam em Java, a Plataforma (mais precisamente, plataformas: Java SE, Java ME e Java EE). Dezenas de linguagens que rodam sobre a VM Java são listadas pelo site "Languages for the JVM" (veja links). Mas na prática a Plataforma Java sempre foi muito dominada pela Linguagem Java.

Isso é uma evidência da qualidade da linguagem. Por outro lado, também é efeito do posicionamento histórico da Sun e do Java Community Process, que sempre deram suporte oficial apenas à linguagem Java. E como a comunidade Java tem um forte senso de aderência a padrões, isso sempre dificultou a vida de outras linguagens.

A falta de suporte oficial a linguagens diferentes tem impacto em áreas como o oferta de ferramentas, e na integração entre linguagens. Por exemplo, embora exista uma implementação de Python para a plataforma Java (Jython), não podíamos escrever uma aplicação web contendo scriptlets Python em páginas JSP, nem contar com o auxílio dos principais IDEs do mercado para facilitar esta opção de desenvolvimento.

Mas essa história está mudando. A JSR-223 ("Scripting para a Plataforma Java") define um mecanismo para suporte a linguagens de scripting (veja o quadro "Linguagens de scripting e linguagens dinâmicas"), com previsões específicas para integração em aplicações Java EE (pacotes war e ear) e uso em JSPs. Essa JSR especifica a API javax.script, que permite a um programa qualquer que rode na plataforma Java, acionar um "engine" capaz de interpretar scripts em qualquer linguagem. O futuro Java SE 6.0 (Mustang) já inclui a javax.script API e um plug-in para a linguagem JavaScript, embutindo o Mozilla Rhino (veja links).

Ultimamente tem-se falado muito em linguagens dinâmicas, e é interessante observar que esta tendência deve muito ao Java. Embora a linguagem Java seja estaticamente tipada, ela compartilha várias características de linguagens dinâmicas: código seguro, portabilidade, paradigma orientado a objetos, máquinas virtuais com gerenciamento de memória automático (garbage collection) e compilação just-in-time. Não há ainda uma JSR específica para suporte a linguagens dinâmicas, mas isso não vai tardar – o quadro "Uma JVM mais Dinâmica" mostra detalhes.

Boa parte dos recursos popularizados por Java já existia, mas o Java foi a primeira linguagem de grande sucesso a reunir todas essas idéias. Sem o Java hoje ainda estaríamos presos a compiladores C/C++, caçando overflows de buffer e tendo trabalho para portar código de um sistema operacional para outro ligeiramente diferente. De fato, sem o Java não haveria muito espaço para novidades como o Ruby on Rails e, do outro lado do muro, na ausência de Java a Microsoft dificilmente teria evoluído para uma arquitetura como a sua .Net.

O papel do Groovy

O Groovy é uma nova linguagem que pertence a ambas as categorias: é uma linguagem de scripting e uma linguagem dinâmica.

A primeira pergunta que se faz em relação ao Groovy é: para que outra linguagem dessas, se já temos Ruby, Python, JavaScript e tantas outras? A reposta é que o Groovy destaca-se por ser projetada especificamente para a plataforma Java e sua sintaxe é baseada, tanto quanto possível, na da linguagem Java. Além disso, o Groovy também é projetado para explorar as características da JVM (como os tipos de dados fundamentais suportados pelo bytecode Java) e as APIs do Java SE. São fatores facilitam muito o aprendizado dessa linguagem para quem já conhece Java, simplificam o porte de código entre ambas as linguagens e a integração entre módulos escritos em Java e Groovy.

Por exemplo, o Groovy utiliza as interfaces de collections do Java SE (APIs de java.util). Uma lista do Groovy é uma java.util.List, permitindo o uso dos mesmos métodos (get(), size() etc.), incluindo também extensões do Groovy como each(). Um método Java que espera um parâmetro do tipo List pode ser invocado de um programa Groovy, recebendo como parâmetro uma lista do Groovy, e esta invocação irá funcionar sem necessidade de nenhuma conversão de dados ineficiente.

Essa compatibilidade também permitiu ao Groovy tornar-se uma alternativa para os desenvolvedores que apreciam a linguagem Java, mas que gostariam de ter apenas algumas alterações na sua sintaxe, que por algum motivo têm pouca ou nenhuma chance de serem aprovadas numa versão futura da Java Language Specification. Por exemplo, a facilidade de declarar variáveis omitindo seu tipo é algo que podemos garantir que jamais seria aprovada. Além de ser uma sugestão ser radicalmente contrária a princípios fundamentais de design da linguagem, é impossível implementá-la sem nenhuma quebra de compatibilidade com o gigantesco volume de código Java já existente. Mas o Groovy – que não possui tal legado – pode implementar a tipagem dinâmica e também várias outras amenidades que veremos aqui.

Apresentando o Groovy

Vamos seguir a tradição começando com o clássico "Alô Mundo". No caso do Groovy, este programa pode ser escrito com uma única linha, mas ainda assim pode nos dizer muita coisa sobre a linguagem:

 

println("Alo, Groovy")

 

Para executar esse código obtenha a versão mais recente do Groovy em dist.codehaus.org/groovy/distributions e descompacte em um diretório de fácil acesso (por exemplo, c:\Groovy no Windows). Há várias maneiras possíveis de executar nosso "Alo mundo", usando utilitários na pasta bin. A mais direta é executando a shell interativa do Groovy: entre na pasta bin, digite groovsh e entre com o comando println("Alo, Groovy"). Depois digite go (use exit para sair.). Outra opção é executar a shell gráfica groovyConsole. Digite o script e pressione Ctrl+Enter para executar (veja a Figura 1).

 

image001.png
Figura 1
. Execução do exemplo mínimo usando o GroovyConsole

Mas também é possível compilar o programa: grave o script com o nome alo.groovy, compile com groovyc alo.groovy, e execute com groovy alo. O código gerado pelo groovyc é um .class normal; o utilitário groovy é somente um .bat ou script de shell que invoca a JVM após confirmar o classpath com os jars de runtime do Groovy. Usuários do Eclipse podem também instalar o GDT – Groovy Developer Tools, um plug-in simples, ainda em estado incipiente, que facilita a edição e execução de scripts Groovy.

Neste programa mínimo, já podemos observar algumas características de linguagens dinâmicas. Não precisamos declarar classes e métodos, podendo executar código num escopo global (no caso, o Groovy cria uma classe com um método main() implicitamente). A terminação de statements por ";" é opcional, exceto para separar statements na mesma linha. O acesso a APIs importantes é facilitado; no caso, println() não precisa ser invocado a partir de System.out (veja também a seção "O Groovy JDK e a API groovy.*").

Vejamos mais algumas facilidades:

 

def alo (nome) {

  def sobrenome = "Doederlein"

  println("Alo, ${nome.toUpperCase()} ${sobrenome}")

}

alo("Osvaldo")

®

Alo, OSVALDO Doederlein

 

(Neste e nos próximo exemplos, usaremos o ícone ® para indicar as saídas da execução). Aqui vemos mais recursos dinâmicos: podemos definir variáveis e métodos sem declarar tipos (de variáveis atributos, parâmetros ou retornos). Dentro de strings a sintaxe ${...} dá acesso ao valor formatado de variáveis ou expressões arbitrárias, de forma semelhante às expressões da Expression Language (EL) do JSP 2.0.

 

def jm () {

  def editores = ["Leonardo", "Lozano", "Osvaldo"]

  def artigos = [29:"Derby", 30:"Performance"]

  def paginas = 1..68

  artigos.each({num, tit -> println("JM${num} - ${tit} (${editores[2]})")})

  editores.each({println("Editor: ${it}")})

  paginas.contains(40)

}

println(jm())

®

JM29 - Derby (Osvaldo)

JM30 - Performance (Osvaldo)

Editor: Leonardo

Editor: Lozano

Editor: Osvaldo

true

 

O Groovy oferece sintaxes facilitadas de inicialização para três tipos de coleções – List, Map e Range (este último, que não tem equivalente em Java, representa todos os valores num intervalo). O comando return é suportado, mas desnecessário: métodos não-void retornam o resultado da última expressão avaliada; portanto, a invocação a jm() retorna true.

Os métodos each() ilustram outra funcionalidade comum em linguagens dinâmicas. É o suporte a closures, as quais no exemplo permitem iterar coleções de forma muito simples. Uma closure é semelhante a uma inner class do Java, porém mais poderosa e com sintaxe mais simples, na forma { parâmetros  ->  statements}.

No exemplo anterior, como artigos é um Map (iterável como uma lista de pares chave-valor), passamos para each() uma closure com dois parâmetros. Na iteração de editores basta um argumento para acessar cada elemento da lista. E já que é comum este caso de closure com um único argumento, podemos omitir a declaração do argumento e usar o identificador default it. Veremos mais sobre esse recurso no tópico "Closures", adiante.

Classes

O Groovy é uma linguagem orientada a objetos baseada em classes, sendo nesse aspecto idêntica à linguagem Java: são suportadas classes (com herança simples) e interfaces (com herança múltipla), overriding de métodos etc. A maior diferença é que o Groovy não possui tipos primitivos – tudo é um objeto. Vejamos outro exemplo:

 

class Artigo {

 private @Property titulo

 private @Property String autor

 def getAutor () { return autor }

 def setAutor (autor) { this.autor = autor.toUpperCase() }

}

groovy = new Artigo(titulo:"Groovy", autor:"Osvaldo")

println(groovy.autor)

®

OSVALDO

 

A declaração de classes começa de forma similar à feita em Java, mas acrescenta novidades. A anotação @Property declara uma propriedade do tipo JavaBean: um atributo exposto por métodos get/set. Não precisamos escrever implementações triviais de métodos get/set, pois a anotação @Property dá conta disso. Contudo podemos escrevê-las se quisermos implementações não-default, como no caso de setAutor(). Neste caso será necessário implementar tanto o get quanto o set, pois se implementarmos apenas um deles, o outro não será disponibilizado pela tag @Property.

O Groovy suporta as mesmas sintaxes do Java para acessar atributos e invocar métodos ou construtores, mas não precisamos definir construtores que recebem os valores iniciais dos atributos. É oferecida uma sintaxe alternativa de construção, onde passamos o nome e o valor para cada propriedade a ser inicializada. Esta sintaxe causa a invocação do construtor mais apropriado, conforme a compatibilidade da lista de inicialização com os argumentos de cada construtor. Caso o construtor selecionado não possua argumentos para algumas das propriedades, elas serão inicializadas pela invocação dos métodos set. No exemplo, como nossa classe Artigo só possui o construtor default (sem argumentos, gerado automaticamente da mesma forma que em classes Java), o Groovy invocará os métodos setTitulo() e setAutor() de forma automática.

Quanto aos atributos, o Groovy também facilita as coisas: podemos escrever groovy.autor para ler o atributo Artigo.autor, embora o atributo seja private. O Groovy reconhece essa sintaxe como um atalho para os métodos get/set. No caso, getAutor() será invocado.

As mesmas opções de visibilidade do Java estão disponíveis em Groovy: public, protected e private, além da opção default, "package-private" (visibilidade de pacote). Somente no caso de propriedades, o default é diferente: os métodos get/set, se implementados explicitamente sem uma opção de visibilidade, serão public.

Em Groovy não é obrigatório declarar tipos de atributos ou tipos de retorno. Mas, para reconhecer uma declaração de atributo ou método, é exigido pelo menos um dos seguintes elementos: um tipo (como String), uma opção de visibilidade (como private) ou, na falta destes, a palavra-chave def. Pode-se começar um programa utilizando só def, e posteriormente acrescentar declarações de visibilidade ou de tipos à medida que o código amadurece.

Ressalte-se que as declarações estáticas de tipo não são inúteis, só por serem opcionais. Como em Java, se forem fornecidas declarações de tipo, o groovyc poderá detectar mais erros em tempo de compilação. O código também será mais eficiente, pois sem declarações de tipo o bytecode gerado utiliza java.lang.Object como tipo de todos os atributos, variáveis e parâmetros – o que exige conversões e invocações dinâmicas que poderiam ser evitadas na presença de tipos estáticos.

Operadores

Na área de sintaxe vimos que o Groovy segue a maioria dos passos da linguagem Java. Há, no entanto, um grande desvio pelo menos num tópico: operadores. O Groovy permite redefinir operadores para facilitar o uso de objetos que possuam uma semântica adequada, e já oferece operadores que facilitam a utilização de muitas APIs importantes. Por exemplo:

 

def lista = [1,2,3]

lista << 4

lista *= 2

lista += ["a", "b"]

println(lista)

println(lista[1..9])

®

[2, 3, 4, 1, 2, 3, 4, a, b]

 

Aqui podemos ver alguns operadores suportados pelas listas: lista<<item equivale a lista.add(item), lista*N duplica a lista N vezes, lista1 + lista2 concatena duas listas, e ...

Quer ler esse conteúdo completo? Tenha acesso completo