Manual Groovy Para Devs Java
manual-groovy-para-devs-java
User Manual:
Open the PDF directly: View PDF
.
Page Count: 30

CAPÍTULO13
GroovyéalinguagemoficialdoSpockframework,porissoé
fundamentalterumaproficiênciamínimacomela.Estecapítuloé
paraoleitorquepossuipoucaounenhumaexperiênciacomaessa
linguagem.ÉumguiafocadoemdesenvolvedoresJava,paraque
façamo"dexpara"(emJavaédaquelejeitoeemGroovyédesse)e
tenhammaiorfacilidadenaescritadostestescomSpock.
AlinguagemGroovyfoicriadaem2003porJamesStrachane
hoje possui seu código aberto sendo mantida pela Apache
Software Foundation desde 2015. É uma das linguagens cujo
compiladorgeraarquivosexecutáveispelaMáquinaVirtualJava,
a JVM. Ou seja, aquivos escritos nessa linguagem, quando
compilados,geramarquivos.class executáveisporqualquerJVM
deversãodeJavacompatível.Naprática,issosignificaque:
Se uma IDE possui suporte a Java e Groovy, as classes
escritasnessasduaslinguagensconseguemtrabalharentre
APÊNDICEA-GUIADE
GROOVYPARA
DESENVOLVEDORESJAVA
13.1COMOFUNCIONAOGROOVY
13APÊNDICEA-GUIADEGROOVYPARADESENVOLVEDORESJAVA195

sideformatransparente;
Bibliotecas escritas em Groovy podem ser usadas por
projetosJavaevice-versa.
Essefuncionamentoérepresentadonafiguraaseguir.
Figura13.1:FuncionamentodoGroovynaJVM.
É por isso que o Spock, que possui como linguagem de
programação o Groovy, pode ser usado para criar testes para
projetos em Java, Scala, Kotlin, Jython, JRuby ou qualquer outra
linguagemquecompileparaaJVM.Consequentemente,tambémé
possível criar testes para projetos que usam qualquer framework
Java como Spring MVC/Boot, Play, Struts, VRaptor etc. e até
mesmoparaprojetosAndroid.
Uma característica que reduz a "curva de aprendizado"dessa
linguagem para desenvolvedores Java é a proximidade entre as
sintaxes e a possibilidade de escrever um código Groovy
exatamentecomoescreveriaemJava,quevaicompilarefuncionar
19613.1COMOFUNCIONAOGROOVY

perfeitamentenaimensamaioriadoscasos.Ouseja,nadúvidaem
determinadotrechodecódigoGroovy,façaexatamentecomofaria
emJava.
Aseguir,algunsdosrecursosecaracterísticasdoGroovyque
podemajudarmuitonacriaçãodetestescomSpock.
A sintaxe do Groovy é muito parecida com do Java. Porém,
algumas diferenças na sintaxe do Groovy podem melhorar a
produtividade no desenvolvimento. A seguir, várias dessas
diferençasserãoapresentadas.
Pontoevírgulasãoopcionaisnofinaldalinhadecódigo
Casoodesenvolvedorqueiraabrirmãodeusarpontoevírgula
(;) ao final de cada linha, o compilador Groovy vai considerar a
quebradelinhacomoofimdalinhadecódigo.Porém,seelafor
utilizada,seráofinalexplicitodalinhadecódigo,comoéemJava.
É possível usar linhas com e sem ponto e vírgula no final num
mesmoarquivoGroovysemproblemaalgum.
System.outopcional
Casosejanecessárioimprimiralgonasaídapadrão,épossível
usarosSystem.out.print()eSystem.out.println()como
em Java. Porém, como o System.out é opcional em Groovy,
podem ser substituídos apenas por print() e println(),
13.2 CARACTERÍSTICAS E RECURSOS DO
GROOVY
Diferençasnasintaxe
13.2CARACTERÍSTICASERECURSOSDOGROOVY197

respectivamente.
Parêntesessãoopcionaisnainvocaçãodemétodos
Em Groovy, não é obrigatório o uso de parênteses na
invocaçãodemétodos,sejamelescomousemparâmetros.Como
exemplo, temos o código a seguir, que imprime textos na saída
padrãousandooprintlncomesemparênteses.
println("Nãosoudonodomundo")
println"Massoufilhododono"
Outroexemplo:Ocódigoaseguirexemplificaainvocaçãode
métodos da classe java.lang.Math sem parênteses, primeiro
comumdepoiscomdoisparâmetros.
doubleraiz144=Math.sqrt144//retornaráaraizde144:12
doubledoisAoCubo=Math.pow2,3//retornará2aocubo:8
Ratificando:Onãousodeparênteseséopcional.Casooleitor
acheessetipodesintaxeconfusa,podeusarosparêntesestalcomo
usanainvocaçãodemétodosemJava.
Porpadrão,asclassessãopúblicas
Quantas vezes você criou uma classe que não deveria ser
public?EmGroovy,asclassessãopúblicasporpadrão.Ouseja,
aclasseFutebolistaaseguirserácompiladacomopúblicapela
JVM:
//Futebolista.groovy
classFutebolista{//emtempodeexecução:"publicclassFuteb
olista"
//atributos,métodos,etc.
}
Porpadrão,osatributossãoprivados
19813.2CARACTERÍSTICASERECURSOSDOGROOVY

Quantas vezes você criou um atributo de instância que não
deveriaserprivate?EmGroovy,osatributossãoprivadospor
padrão. Ou seja, na classe Groovy Futebolista a seguir, os
atributosnomeevalorPasseserãoconsideradosprivadospela
JVM:
//Futebolista.groovy
classFutebolista{
Stringnome//emtempodeexecução:"privateStringnome"
doublevalorPasse//emtempodeexecução:"privatedoubleval
orPasse"
}
Porpadrão,osmétodossãopúblicos
Você criou muito mais métodos public, private ou
defaultemsuasclasses?EmGroovy,osmétodossãopúblicos
porpadrão.Ouseja,aclasseGroovyFutebolistaaseguirteria
um método considerado público (treinar()) e outro privado
(fugirDaConcentracao())pelaJVM:
//Futebolista.groovy
classFutebolista{
Stringnome
doublevalorPasse
voidtreinar(){//emtempodeexecução:"publicvoidtreinar
()"
//treinando...
}
privatevoidfugirDaConcentracao(){
//fazendoafestaforadaconcentração...
}
}
Épossívelatribuirvaloresdeatributosnacriaçãodeobjetos
Em Java, quando queremos criar uma instância que já tenha
13.2CARACTERÍSTICASERECURSOSDOGROOVY199

determinadosvaloresematributos,énecessáriocriarconstrutores
paraisso.EmGroovy,todaclassepossuiumconstrutorqueaceita
um Map como argumento (porém, com colchetes opicionais).
Assim,épossívelindicarosargumentosquequisereseusvalores.
Por exemplo, seria possível instanciar um objeto do tipo
Futebolistacomonosexemplosdocódigoaseguir:
defjogadorA=newFutebolista(nome:'ZeBoleiro',valorPasse:1
00)
defjogadorB=newFutebolista(valorPasse:100,nome:'ZeBoleir
o')
defjogadorC=newFutebolista(nome:'ZeBoleiro')
defjogadorD=newFutebolista(valorPasse:100)
defjogadorE=newFutebolista([nome:'ZeBoleiro',valorPasse:
100])
MapmapaLoko=//Mapcriadoemalgumlugar...
defjogadorF=newFutebolista(mapaLoko)
Métodospodemterumafraseentreaspascomonome
Emclassesdetestesautomatizados,normalmenteummétodo
testaumcenário.Imagineumcenariochamado"ovalordopasse
do jogador não pode ser menor ou igual a 0(zero)". Se fossemos
nomear um método da maneira convencional, ou seja, uma
palavrasóemcamelCase,seunomeseriaalgocomo...
defvalorPasseJogaodorNaoAceitaZeroOuMenos(){...}
Não acha que é um nome longo e difícil de ler? Uma
alternativaseriareduzironomeepor a descrição do cenário no
JavaDocdométodo.Porém,casootestefalhe,éseunomequeé
impressonologdaexecuçãodostestes.
UsandoGroovy,onomedométodopoderiaser...
def'valordopassedojogadornãopodesermenorouiguala0'()
20013.2CARACTERÍSTICASERECURSOSDOGROOVY

{...}
Usando esse recurso, o cenário do teste fica literalmente
descrito no nome do método. Outra vantagem é que se o teste
falhar, o log de execução conterá os cenários e não nomes de
métodoslongose/oudifíceisdeler.
Podem ser usadas aspas simples ou duplas e até mesmo
caracteresespeciais(letrasacentuadas,cê-cedilha,etc.)podemser
usados.
Parareduzir o tempodeprogramação,em Groovy é possível
criarobjetosemétodoscomooperadordef,usandoorecursoda
tipagemdinâmica.Emcasodemétodos,épossívelomitirotipo
dosparâmetros.Vejamosexemplosaseguir.
Noexemploaseguir,foiusadaatipagemdinâmicaparaambos
osmétodos.Emtempodeexecução,aJVMvaiconsiderarqueeles
retornamObject.
defmetodoX(p1,p2){//emtempodeexecução:"publicObjectm
etodoX(Objectp1,Objectp2)"
//return...;
}
defmetodoY(defn1,defn2){//emtempodeexecução:"public
ObjectmetodoY(Objectn1,Objectn2)"
//return...;
}
Seométodoforstatic,atémesmoodeféopcional,como
nosexemplosaseguir,cujosmétodosserãoconsideradospúblicos,
estáticosecomretornoObjectemtempodeexecução:
staticmetodoX(p1,p2){//emtempodeexecução:"publicstati
Atipagempodeserestáticaoudinâmica
13.2CARACTERÍSTICASERECURSOSDOGROOVY201

cObjectmetodoX(Objectp1,Objectp2)"
//return...;
}
staticdefmetodoY(defn1,defn2){//emtempodeexecução:"
publicstaticObjectmetodoY(Objectn1,Objectn2)"
//return...;
}
Se o desenvolvedor preferir, pode deixar a declaração do
métodooudosargumentosestática,comonosexemplosaseguir:
DoublemetodoX(n1,n2){//emtempodeexecução:"publicDoubl
emetodoY(Objectn1,Objectn2)"
//return...;
}
DoublemetodoY(Doublen1,n2){//emtempodeexecução:"publi
cDoublemetodoY(Doublen1,Objectn2)"
//return...;
}
voidmetodoZ(Doublen1,Doublen2){//emtempodeexecução:"
publicvoidmetodoZ(Doublen1,Doublen2)"
//return...;
}
Objetostambémpodemserdefinidoscomesserecurso,como
no exemplo a seguir onde primeiro é criado um BigDecimal
declaradocomtipagemdinâmicae,depoisumoutrocomtipagem
estática.
defnumero0=newBigDecimal(0)//emtempodeexecução:BigDec
imalnumero0=...
BigDecimalnumero1=newBigDecimal(1)
UmaboanotíciaparaodesenvolvedoréqueasprincipaisIDEs
Java do mercado - Eclipse, IntelliJ e NetBeans - sugerem os
métodos e atributos do tipo usado na instanciação. Ou seja, no
últimocódigo,apósnumero0.essasIDEsiriamsugerirmétodose
20213.2CARACTERÍSTICASERECURSOSDOGROOVY

atributosdaclasseBigDecimal.
EmJava,ooperador==verificaseosmembroscomparados
são,literalmente,omesmoobjeto,amesmainstância.Parasaber
sepossuem conteúdo igual, deve-se usar o método .equals().
InclusiveécomumqueiniciantesemJavatentemcompararduas
Strings de mesmo conteúdo com == e acabam recebendo um
falsecomoresultado.
Acreditandoqueamaioriadosdesenvolvedoresfazmuitomais
comparaçãodeconteúdodoquedereferênciadeinstânciaemseus
projetos,osarquitetosdalinguagemGroovydefiniramque,nela,o
==correspondesseao.equals()doJava.Vejamosoexemploa
seguir,ondetexto1etexto2têmseusconteúdoscomparados:
Stringtexto1=//valorrecebidoemtempodeexecução
Stringtexto2=//valorrecebidoemtempodeexecução
defteste=texto1==texto2
//seoconteúdode"texto1"e"texto2"foremiguais,"teste"
será"true"
Casoexistaanecessidadedesabersedoisobjetossãoamesma
instância,deve-seusarométodo.is(),conforme o exemploa
seguir:
Stringtexto1=//valorrecebidoemtempodeexecução
Stringtexto2=//valorrecebidoemtempodeexecução
Stringtexto3=texto1
defteste1e2=texto1.is(texto2)//"false"
defteste1e3=texto1.is(texto3)//"true"
Operador==fazopapeldo.equals()
Concatenação de valores String mais dinâmica e
simples
13.2CARACTERÍSTICASERECURSOSDOGROOVY203

Em Groovy, um objeto String pode ser instanciado com
aspassimples,comonoexemploaseguir:
Stringpoema='Nascurvasdoteucorpocapoteimeucoração'
Porém, ao se fazer isso, perde-se a possibilidade de usar a
capacidade de concatenações dinâmicas do Groovy, que só é
possívelquandoseusaaspasduplas.Bastausarooperador$eo
nomedoobjetoaserconcatenadonotexto.Vejamosoexemploa
seguir, onde time1,time2 e dias serão concatenados no
conteúdodeanuncio:
deftime1='Tabajara'
deftime2='Sayajins'
defdias=7
defanuncio="Ojogoseráentre$time1e$time2.Faltam$diasdi
as,nãopercam!"
//"OjogoseráentreTabajaraeSayajins.Faltam7dias,nãoper
cam!"
Amontagemdeumtextopodeincluirresultadosdainvocação
demétodose/oudeoperaçõesmatemáticas.Assimcomoorecurso
anterior,épossívelexecutarmétodoseoperaçõesmatemáticase
concatenar seus resultados em String Groovy. Nesse caso, a
expressão deve estar dentro de ${}. No exemplo a seguir, o
resultadodetoUpperCase()emtime1 e desubstring(4)
sobretime2, bem comooresultadode valordivididopor 2
serãoconcatenadosnoconteúdodeanuncio.
deftime1='Tabajara'
deftime2='Sayajins'
defvalor=20
defanuncio="Ojogoseráentre${time1.toUpperCase()}e${time2
.substring(4)}.Mulherespagam${valor/2},nãopercam!"
//"OjogoseráentreTABAJARAejins.Mulherespagam10,nãoper
cam!"
20413.2CARACTERÍSTICASERECURSOSDOGROOVY

Casoexistaanecessidadedemontarumtextoquedevaestar
tãoassociadoaumobjetoaopontoqueseuconteúdoacompanhe
alteraçõesfeitasnesseobjeto,pode-selançarmãodeoutrotipode
concatenaçãomais dinâmica ainda,verificando o valor atual de
umobjetosemprequeaStringforinvocada.Paraisso,oobjeto
deveestardentrode${->}.
Nocódigodopróximoexemplo,associamostime1etime2
àanunciodetalformaque,semprequeanuncioforusada,seu
conteúdo pode variar, de acordo com o conteúdo de time1 e
time2.
deftime1='Tabajara'
deftime2='Sayajins'
deffrase="Ojogoseráentre${->time1}e${->time2}"
//nessemomento,"anuncio"é
//"OjogoseráentreTabajaraeSayajins"
time1='Remo'
time2='Paysandu'
//apartirdessemomento,"anuncio"passaaser
//"OjogoseráentreRemoePaysandu"
Quem já precisou escrever uma instrução SQL ou criar um
mock de um JSON em Java já deve ter feito todo tipo de
malabarismo para que uma String de várias linhas não fique tão
ilegívelnocódigo.EmGroovyexisteorecursodemúltiplaslinhas.
Bastainiciarousartrêsaspassimplesouduplasparainstanciar
umString.
No exemplo de String de múltiplas linhas a seguir, é criada
umainstruçãoSQLcom3linhas.
defconsultaTimesSemTitulo='''
Stringpodemtermúltiplaslinhassemconcatenações
13.2CARACTERÍSTICASERECURSOSDOGROOVY205

select*fromtb_time
wherenum_titulos=0
orderbydt_cricaodesc
'''
Caso você precise criar uma String de múltiplas linhas com
concatenaçãodemétodose/ouoperaçõesmatemáticas,bastausar
trêsaspasduplasnoslimitesdaString.Oexemploaseguirmonta
uma instrução SQL de múltiplas linhas concatenando seu
conteúdocomuf.
defuf='AM'
defconsultaTimesAmazonas="""
select*fromtb_time
whereestado=${uf}
"""
A interoperabilidade de Groovy com classes Java é total,
contantoqueasversões de Java usadas na compilação de ambos
sejam compatíveis. Trata-se da mesma restrição entre classes de
arquivosescritosemJava.
Para exemplificar essa interoperabilidade, consideremos a
classe Java apploko.pjava.Futebolista , que contém três
atributossimples,umconstrutorquealteratodoseleseseusgetters
esetters:
packageapploko.pjava
//imports
publicclassFutebolista{
privateStringnome;
privatedoublehabilidade;
privatedoublevelocidade;
InteroperabilidadecomclassesJava
20613.2CARACTERÍSTICASERECURSOSDOGROOVY

publicFutebolista(Stringnome,doublehabilidade,doubleveloc
idade){
//atribuiçõessimples
}
//gettersesetterspúblicosdetodososatributos
}
A classe Groovy FutebolistasLokos a seguir usa a classe
Javaapploko.pjava.Futebolista de forma transparente:uma
instânciaécriadausandoseuconstrutoreumdeseusmétodos,o
setNome(),éinvocado.
importapploko.pjava.Futebolista
//demaisimports
classFutebolistasLokos{
FutebolistacreateHabilidoso(){
defhabilidoso=newFutebolista("Romário",95,90)
habilidoso.setNome("Romário11")
returnhabilidoso
}
}
Se o projeto utilizar Maven ou Gradle, qualquer classe das
dependências também será acessível de forma transparente pelas
classesGroovy.
Se o leitor já trabalha com Java há algum tempo já deve ter
vistoo java.lang.NullPointerException(vulgoNPE) várias
vezesquandoexecutavaseuprojeto.Issoocorrequandotentamos
invocarum método deumobjetonulo(null), oqueacontece
com uma certa frequência porque é muito trabalhoso(e fácil de
esquecer)verificar a cadagetXXX() se no temos um null. Em
Groovyesseproblemaésimplesderesolver,poisexisteochamado
NullPointerExceptionémuitofácildeserevitado
13.2CARACTERÍSTICASERECURSOSDOGROOVY207

operador de navegação segura(Safe navigation operator), que
previne o NPE apenas com o uso de ? antes do .. Vide o
próximocódigo.
defjogador=//instanciadoemtempodeexecuçãoapartirdob
ancodedados,porexemplo
defpais=jogador?.getTime()?.getPais()?.getNome();
No último código, caso jogador ou getTime() ou
getPais()sejanull,oobjetopais simplesmentereceberá
null,semNPE.Casoooperador?nãosejausado,oriscode
NPEéomesmodeumaclasseJava.
A manipulação de coleções em Java é considerada verbosa
demais por desenvolvedores acostumados com linguagens como
PHP,Python,Rubye JavaScript. Em Groovy,amanipulaçãode
coleções é bem simples, se comparada com Java, conforme
apresentadoaseguir.
Criando uma coleção com valores Em Java, para criar uma
coleçãocomvaloresdeve-serecorreraclassesutilitárias,sejamelas
do Java ou de bibliotecas de terceiros. Em Groovy, esse tipo de
operação é muito simples, como nos exemplos a seguir. No
próximocódigo,háuma exemplo da criação de uma List em
Groovy. Ele cria uma instância de ArrayList com 4 (quatro)
elementos.
defposicoes=['goleiro','zagueiro','meia','atacante']
O interessante no código anterior é que as IDEs Eclipse e
IntelliJinferem o tipo de objeto da coleção quando se cria uma
usandotodososelementosdomesmotipo,Assim,quandotentara
Manipulaçãodecoleçõesémuitosimples
20813.2CARACTERÍSTICASERECURSOSDOGROOVY

recuperação de elementos dessa coleção, a IDE exibirá que o
retorno seria do tipo String . Caso não consigam fazer a
inferência,acoleçãoserátratadacomodeObject.
Nopróximocódigo,háum exemplo da criação de um Map
em Groovy. Ele cria uma instância de HashMap com 3 (três)
elementos, com as chaves derrotas,empates e vitorias
cujosvaloressão0,1e3,respectivamente.
defcampanha=['derrotas':0,'empates':1,'vitorias':3]
Um detalhe bem útil em Groovy é a possibilidade de ser
opcionalousodeaspasquandoaschavesdomapasãoString.
Portanto,nocódigotodasastrêschavespoderiamestarsemaspas,
comonocódigoaseguir.
defcampanha=[derrotas:0,empates:1,vitorias:3]
Criando coleções vazias Em Java, para criar uma coleção
vazia,devemos usar construtorescomofaríamos para qualoutro
tipodeclasse.EmGroovy,épossívelusarosmesmosconstrutores,
porém as principais coleções podem ser criadas com o uso de
operadores, como nos exemplos a seguir. No código Groovy a
seguirháumexemplodecriaçãodeListvazia,implementada
comoÀrrayList.
defposicoes=[]
NocódigoaseguirháumexemplodecriaçãodeMapvazia
implementadacomoHashMapemGroovy.
defpontuacoes=[:]
Adicionando um elemento a uma coleção Em Java, para
adicionarelementosa uma coleção devemos recorrer a métodos,
13.2CARACTERÍSTICASERECURSOSDOGROOVY209

comoadd()ouput().EmGroovyépossívelusarexatamente
osmesmos métodos, porémtambémépossível usar operadores,
comonosexemplosaseguir.Nopróximocódigo,háumaexemplo
dacriaçãodeumaListvaziaeposteriorinclusãodeelementos
nela.
defposicoes=[]
posicoes+='goleiro'
posicoes+='zagueiro'
Há três maneiras de incluir um elemento num Map em
Groovy,todasexemplificadosnocódigoaseguir.
defcampanha=[:]
campanha.derrotas=0//chave:'derrota'/valor:0
campanha['empates']=1//chave:'empate'/valor:1
campanha+=[vitorias:3]//chave:'virotia'/valor:3
RecuperaçãodeitensdeumacoleçãoEmJava,pararecuperar
um item de uma coleção devemos recorrer a métodos, como o
get() . Em Groovy é possível usar exatamente os mesmos
métodos, porém também é possível usar operadores, como nos
exemplos a seguir. No próximo código, há exemplos da
recuperaçãodeumelementodeumaList.
defposicoes=['goleiro','zagueiro','meia','atacante']
println(posicoes[0])//imprimiria'goleiro'
println(posicoes[2])//imprimiria'meia'
HáduasmaneirasderecuperarumelementodeumMapem
Groovy,todasexemplificadosnocódigoaseguir.
defcampanha=[derrota:0,empate:1,vitoria:3]
campanha.derrota//0
campanha['vitoria']//3
Recuperando o último elemento de uma lista ou vetor
Imagige que você precise do último elemento em uma lista mas
21013.2CARACTERÍSTICASERECURSOSDOGROOVY

não sabe quantos elemento ela conterá no momento em que
precisardessainformação.EmJavaéprecisoverificarotamanho
dalistaeusarseuvalormenos1pararecuperaroúltimoelemento
deumalista.EmGroovy,háummétodochamadolast() que
abstraiisso e recuperaoúltimoelemento de uma lista,comono
exemploaseguir.
deflistaDinamica=//recuperadadeformadinâmica(ex:deumba
ncodedados)
defultimo=listaDinamica.last()
Ométodolast()nãoexisteemMapas,estandodisponível
apenasparalistasevetores.
Éumatarefamuitocomumrealizariterações(repetições)para
resolver problemas computacionais. Essas iterações são
comumente baseadas em valores númericos, nos elementos de
umacoleçãoounaslinhasdeumtexto.Aseguir,veremoscomo
essasiteraçõesmaiscomunspodemserfeitasemGroovy.
IteraçãoapartirdenúmerosNanecessidadedarepetiçãode
umtrechode código determinada por um númeroNemJava, é
necessáriocriarestruturasderepetiçãocomofor,do-whileou
while. Em Groovy, os números inteiros possuem um recurso
embarcado que atende facilmente essa necessidade. É a closure
times{}.Noexemploaseguir,estáprogramadaumarepetição
deumtrechodecódigopor11(onze)vezes.
11.times{
//ocódigoaquirepetirá11vezes
}
Técnicas de iteração simples e embarcadas para
números,coleçõeseStrings
13.2CARACTERÍSTICASERECURSOSDOGROOVY211

Em vez do número 11, poderíamos ter aplicado o mesmo
recursosobreumavariáveldotipoInteger.Casosejanecessário
saberemquepassodaiteraçãoestamos,bastausaravariávelit,
queéinjetadaautomaticamentenaClosuretimes{}.Elavaide
0aN-1,ondeNéonúmerodeiterações.Ocódigodoexemploa
seguirdemonstracomopoderíamoslançarmãodesserecurso.
defjogadores=[jogador1,jogador2,jogador3]
jogadores.size().times{
println(jogadores[it].getNome())
}
Por questões de legibilidade de código ou para o caso de
iterações aninhadas, é possível definir um nome da variável do
passodaiteração.Noexemploaseguir,usamosonomej.
defjogadores=[jogador1,jogador2,jogador3]
jogadores.size().times{j->
println(jogadores[j].getNome())
}
IteraçãoapartirdeListeSet
Quando há repetição de um trecho de código para cada
elementodeumacoleçãoemJava,énecessáriocriarestruturasde
repetiçãocomofor,do-while,whileouusarosstreams
ou método forEach() do Java 8. Em Groovy, as coleções
possuem um recurso embarcado que atende facilmente essa
necessidade. São as closures each{} e eachWithIndex{} ,
apresentadaseexemplificadasaseguir.Noexemploaseguir,está
programadaumarepetiçãoparaositensdeumaList,mas que
funcionariadamesmaformaparaSet.Oitdentrodaclosureé
onomequecadaelementodalistarecebenaiteração.
defjogadores=[jogador1,jogador2,jogador3]
jogadores.each{
21213.2CARACTERÍSTICASERECURSOSDOGROOVY

println("NomedoJogador:${it.getNome()}")
}
Por questões de legibilidade de código ou para o caso de
iterações aninhadas, é possível definir um nome do elemento na
iteração.Noexemploaseguir,usamosonomejogador.
defjogadores=[jogador1,jogador2,jogador3]
jogadores.each{jogador->
println("NomedoJogador:${jogador.getNome()}")
}
Casosejanecessáriosaberemquepassodaiteraçãoestamos,
podemos usar a closure eachWithIndex . Nesse caso, é
obrigatório indicar os nomes do elemento e da variável que
conterá o índice (passo) da iteração (que começa em 0), como
demonstrado no código do exemplo a seguir, onde chamamos o
elementodejogadoreoíndicedei.
defjogadores=[jogador1,jogador2,jogador3]
jogadores.eachWithIndex{jogador,i->
println("Nomedo${i+1}Jogador:${jogador.getNome()}")
}
IteraçãoapartirdeMapQuandohárepetiçãodeumtrecho
de código para cada elemento de um mapa (Map) em Java, é
necessáriocriarestruturasderepetiçãocomofor,do-while,
whileouusarosstreamsoumétodoforEach()doJava8.
Em Groovy, as coleções possuem um recurso embarcado que
atende facilmente essa necessidade. É a closures each{} ,
apresentadaeexemplificadaaseguir.
Noexemploaseguir,estáprogramadaumarepetiçãoparaos
itensdeumMap.O itdentroda closure é onomequecada
elementodomaprecebenaiteração.Oatributokey,éochavee
ovalue,ovalordoelemento.
13.2CARACTERÍSTICASERECURSOSDOGROOVY213

defcampanha=[derrotas:0,empates:2,vitorias:5]
println("Campanha:")
campanha.each{
println("${it.key}:${it.value}")//exdesaída:"vitorias:
5"
}
Por questões de legibilidade de código ou para o caso de
iterações aninhadas, é possível definir um nome do elemento na
iteração.Noexemploaseguir,usamosonomecampanha.
defcampanha=[derrotas:0,empates:2,vitorias:5]
println("Campanha:")
campanha.each{campanha->
println("${campanha.key}:${campanha.value}")//exdesaída:
"vitorias:5"
}
Épossívelaindadefinirdiretamenteumnomeparaachavee
outroparaovalor,comonopróximocódigo,ondeoschamamos
deresultadoequantidade,respectivamente.
defcampanha=[derrotas:0,empates:2,vitorias:5]
println("Campanha:")
campanha.each{resultado,quantidade->
println("${resultado}:${quantidade}")//exdesaída:"vitor
ias:5"
}
Averificaçãodeverdadeiro/falso(true/false),provavelmente,
amaiscomumemtestesautomatizados.Épossívelfazeressetipo
de verificação de forma simplificada em Groovy conforme as
situaçõesaseguir.
nulléfalseenãonuloétrueQualquerobjetoqueseja
Verificação de "verdadeiro/falso" expandida, porém
facilitada
21413.2CARACTERÍSTICASERECURSOSDOGROOVY

null, quando testado em um assert ou if ou operador
ternárioouatributidoaumavariávelboolean,seráinterpretado
comofalse. Vide o código Groovy de exemplo a seguir, cujo
assertfalharia.
defjogador=null
assertjogador
Nas mesmas situações recém-citadas, qualquer objeto não
nulo, a princípio é true. No código do exemplo a seguir, o
assertpassaria.
defjogador=newFutebolista()
assertjogador
Observe:foiditoqueaprincípio,oresultadoétrue.Existem
casosondeoobjetonãoénulo,massuaverificaçãobooleanapode
serfalse.Essasexceçõesserãoapresentadasaseguir.
Stringvaziaéfalseenãovaziaétrue
Qualquer String que seja vazia("" ou ''), quando
testado em um assert ou if ou operador ternário ou
atributido a uma variável boolean , será interpretado como
false.VideocódigoGroovydeexemploaseguir,cujoassert
falharia.
defnomeNogador=""
assertnomeJogador
Nas mesmas situações recém-citadas, qualquer Stringnão
vazia,seráconsideradotrue.Nocódigodoexemploaseguir,o
assertpassaria.
defnomeNogador="ZéLoko"
assertnomeJogador
13.2CARACTERÍSTICASERECURSOSDOGROOVY215

Número0(zero)éfalse.Qualqueroutroétrue
Qualquer número, qualquer tipo numérico (inclusive
BigDecimal)queseja0(zero),quandotestadoemumassert
ou if ou operador ternário ou atributido a uma variável
boolean,seráinterpretadocomofalse.VideocódigoGroovy
deexemploaseguir,cujosassertsfalhariam.
defnumeroLoko1=0
defnumeroLoko2=0.0
defnumeroLoko3=newBigDecimal(0)
assertnumeroLoko1
assertnumeroLoko2
assertnumeroLoko3
Nas mesmas situações recém-citadas, qualquer número
diferentedezero,mesmonegativo,seráconsideradotrue.
ColeçõesvaziassãofalseenãovaziassãotrueQualquer
coleção(List, SetouMap) quesejavazia(semelementos),
quandotestadoemumassertouifouoperadorternárioou
atributido a uma variável boolean , será interpretado como
false.VideocódigoGroovydeexemploaseguir,cujoassert
falharia.
defjogadores=[]
assertjogadores
Nas mesmas situações recém citadas, qualquer coleção não
vazia,seráconsideradotrue.Nocódigodoexemploaseguir,o
assertpassaria.
defcampanha=[derrotas:0,vitorias:7]
assertcampanha
Métodosembarcadosparaasconversõesmaiscomuns
21613.2CARACTERÍSTICASERECURSOSDOGROOVY

A conversão de tipos é uma operação muito comum nos
problemascomputacionaisatuais,principalmentedevidoàgrande
quantidadedeintegraçãodesistemasqueocorrehoje.EmJava,as
conversões são um tanto verbosas. Em Groovy há métodos de
conversão embarcados para as conversões mais comuns do
cotidiano,comoexemplificadoaseguir.
ConversãodeStringparaqualquertiponumérico
O tipo String em Groovy já possui métodos embarcados
paraaconversãoparatodosostiposnúmericosdaplataformaJava.
Nocódigodeexemploaseguir,variáveisStringsãoconvertidas
com sucesso para
Integer,Long,BigInteger,Float,DoubleeBigDecimal.
deftxtNumericoInteiro='8'
Integerinteiro=txtNumericoInteiro.toInteger()
Longlongo=txtNumericoInteiro.toLong()
BigIntegerbigInteger=txtNumericoInteiro.toBigInteger()
deftxtNumericoReal='8.5'
Floatflutuante=txtNumericoReal.toFloat()
Doubleduplo=txtNumericoReal.toDouble()
BigDecimalbigDecimal=txtNumericoReal.toBigDecimal()
Vale destacar que caso o valor da String não conter um
número válido para a conversão solicitada, uma exceção será
lançada.
ConversãodeStringparaBoolean
O tipo String em Groovy já possui métodos embarcados
para a conversão para Boolean. Os valores "true", "y" e
"1" são convertidos para true . Qualquer outro valor é
convertidopara false. No código do exemplo de conversão a
seguir,todasasconversõesresultariamemtrue,portanto,todos
13.2CARACTERÍSTICASERECURSOSDOGROOVY217

osassertpassariam.
deftextoBoleano='true'
asserttextoBoleano.toBoolean()
textoBoleano='y'
asserttextoBoleano.toBoolean()
textoBoleano='1'
asserttextoBoleano.toBoolean()
No código do exemplo de conversão a seguir, todas as
conversõesresultariam emfalse, portanto, todos os assert
falhariam.
deftextoBoleano='false'
asserttextoBoleano.toBoolean()
textoBoleano='oi'
asserttextoBoleano.toBoolean()
textoBoleano=''
asserttextoBoleano.toBoolean()
A manipulação de datas é uma operação muito comum nos
problemascomputacionais atuais.EmJava,as conversões Date
paraStringeviceversa,bemcomoadicionarousubtrairdiasa
datassãoumtantoverbosas,mesmonaversão8.EmGroovyhá
métodosdeconversãoembarcadosquefacilitamasoperaçõesmais
comunscomdatas,comoexemplificadoaseguir.
Conversão de String para Date Para converter uma
StringparaDate,bastausarométodoembarcadoparse(),
informando o formato (pattern) e o texto a ser convertido. O
códigoa seguir demonstracomoconverterum texto em datano
formato"dd/MM/yyyy".
deftxtDataLoka='01/01/1980'
defdataLoka=Date.parse('dd/MM/yyyy',txtDataLoka)
Manipulaçãodedatasémuitosimples
21813.2CARACTERÍSTICASERECURSOSDOGROOVY

ConversãodeDateparaStringParaconverterumaDate
para String, basta usar o método embarcado format() ,
informando o formato (pattern). O código a seguir demonstra
comoconverterumadataemtextonoformato"dd/MM/yyyy".
defagora=newDate()
deftxtAgora=agora.format('dd/MM/yyyy')
AdicionandoousubtraindodiasdeDateEmGroovy,para
adicionarousubtrairdiasdeumadatapode-sesimplesmenteusar
os operadores + e -, literalmente, como é exemplificado no
códigoaseguir,quesubtrai1diaaumadataedepoisadiciona2
diasaoutra.
defhoje=newDate()//porexemplo:02/01/2000
defontem=hoje-1//01/01/2000
defamanha=ontem+2//03/01/2000
Nocódigoanterior,oobjetohojenãofoialterado.Paraque
adatasejarealmentealterada,épreciso,literalmente,substituirseu
valor,comonoexemploaseguir.
defdataLoka=newDate()//porexemplo:02/01/2000
dataLoka=dataLoka-1//01/01/2000
dataLoka=dataLoka+2//03/01/2000
dataLoka++//04/01/2000
Casoooperador-sejaaplicadoentredatas,oresultadoseráa
diferençadediasentreaprimeiraeasegunda.Ocódigoaseguir
demonstraissoaocompararadatadehojecomadeamanhã.
defhoje=newDate()//porexemplo:01/01/2000
defamanha=hoje+1//02/01/2000
println(amanha-hoje)//resultado:1
println(hoje-amanha)//resultado:-1
IndoparaazerohoradeumaDateEmGroovy,épossívelir
para a zero hora de uma data usando o método embarcado
13.2CARACTERÍSTICASERECURSOSDOGROOVY219

clearTime().Importante:estemétodoalteraadatano qual é
invocado, não retorna uma versão ajustada para a zero hora. O
exemploaseguirajustariaadatanaqualfoiinvocadoparaazero
horadeseudia.
defhoje=newDate()//porexemplo:01/01/200015:30:00
hoje.clearTime()//01/01/200000:00:00
Em Java, um método privado ( private ) não pode ser
invocado,anão sercomusodaReflection API,oquenãoé
tarefa simples. Em Groovy, o acesso a métodos privados é
transparente,sendo possívelinvocá-loscomose fossem públicos.
OcódigodoexemploaseguirédeumaclasseJavaquepossuio
método privado atualizarClassificacao() que é invocado
por seus outros dois métodos que são públicos, o gol() e o
finalizar().
publicclassPartida{
publicvoidgol(Jogadorautor){
//açõesacadagol...
this.atualizarClassificacao();
}
publicvoidfinalizar(){
//açõesaofinalizarpartida
this.atualizarClassificacao();
}
privatevoidatualizarClassificacao(){
//açõesdaatualizaçãodaclassificação
}
}
ParainvocarométodoatualizarClassificacao()deuma
instânciadePartidaapartirdeumaclasseGroovy,bastainvocá-
Acessodiretoamétodosprivados
22013.2CARACTERÍSTICASERECURSOSDOGROOVY

lodiretamente,comosefossepúblico.OtrechodecódigoGroovy
a seguir instancia uma Partida e invoca seu método privado.
InclusiveasIDEsEclipse,IntellijeNetBeanssugeremeaceitama
invocação de métodos privados durante a edição de código
Groovy.
defpartidaLoka=newPartida();
partidaLoka.atualizarClassificacao()
Esserecursoémuitoútilemcasoscomoodoúltimoexemplo,
ondemétodosprivadospossuemmuitarelevância,poispermitea
criaçãodevárioscenáriosdetestediretamenteparaeles.
Em algumas situações, métodos podem fazer ações muito
complexas e/ou que dependem de elementos externos. Como
exemplostemos:acessoabancosdedados,consumodeAPIseuso
de bibliotecas de terceiros. Para casos como esse, pode-se
sobrescreverocomportamentodométodoemtempodeexecução
emGroovy,semanecessidadedeumasubclasseanônimanemde
umainstânciamock.Tomemoscomobaseaclasseaseguircomo
exemplo, a ClienteRestTimesFutebol e seu método
getToken(). Esse método solicitaria um token de autenticação
juntoaumaAPIapartirdeumusuarioeumasenha.
publicclassClienteRestTimesFutebol{
publicStringgetToken(Stringusuario,Stringsenha){
//chamadaàAPIparaobter'token'
}
}
Nocódigoaseguir,definimosqueumadeterminadainstância
Alteraçãodocomportamentodemétodosemclassese
objetosemtempodeexecução
13.2CARACTERÍSTICASERECURSOSDOGROOVY221

de ClienteRestTimesFutebol , a que chamamos de
clienteLoko passa a ter um comportamento diferente para o
métodogetToken(),enquantoqueainstânciaclienteNormal
continuacomseumétodooriginal:
defclienteLoko=newClienteRestTimesFutebol()
cliente1.metaClass.getToken={Stringu,Strings->
return"fake-token"
}
defclienteNormal=newClienteRestTimesFutebol()
deftoken1=clienteLoko.getToken(null,null)//apenasretorna"
fake-token"
deftoken2=clienteNormal.getToken(null,null)//invocariaave
rsãooriginaldo"getToken()"
Caso seja necessário que todos os objetos de uma classe
tenhamocomportamentodeummétodoalterado,épossívelfazer
esse ajuste na classe, consequentemente, em todas as suas
instâncias. Como no exemplo a seguir, que mudaria o
getToken() para qualquer instância de
ClienteRestTimesFutebol:
ClienteRestTimesFutebol.metaClass.getToken={Stringu,Strings
->
return"fake-token"
}
defcliente1=newClienteRestTimesFutebol()
defcliente2=newClienteRestTimesFutebol()
deftoken1=cliente1.getToken(null,null)//apenasretorna"fak
e-token"
deftoken2=cliente2.getToken(null,null)//apenasretorna"fak
e-token"
gettersesetterspodeminvocadosapenascomonome
doatributo
22213.2CARACTERÍSTICASERECURSOSDOGROOVY

Osmétodosgetterse setters fazempartedodia a dia
dos desenvolvedores da plataforma Java. Fazem parte do padrão
JavaBeans
(https://docs.oracle.com/javase/tutorial/javabeans/writing/properti
es.html)esãousadospelosprincipaisframeworksJava.Seuusoé
tãocomumqueasprincipaisIDEsJavapossuemassistentesparaa
rápidacriaçãodessesmétodos.EmGroovy,existeapossibilidade
invocar esses métodos apenas usando o nome do atributo.
valorPasseprivado,acessívelcomumgeteumsetescritos
demaneiracorreta:
publicclassFutebolista{
privatedoublevalorPasse;
publicdoublegetValorPasse(){
returnthis.valorPasse;
}
publicvoidsetValorPasse(doublevalorPasse){
this.valorPasse=valorPasse;
}
}
ParaacessarossetValorPasse()egetValorPasse() em
uma classe Groovy, poderíamos escrever um código como o do
exemploaseguir:
FutebolistajogadorLoko=//obtendooFutebolista
jogadorLoko.valorPasse=50000000
defpasseDoLoko=jogadorLoko.valorPasse
Apesardeparecerquehouveumacessodiretoaoatributo,não
houve. O compilador Groovy verifica se existe um
getValorPasse() na segunda linha e se existe um
setValorPasse(double parametro0) na terceira. Assim, a
versão compilada do último código, para a JVM, seria como o
códigoaseguir:
13.2CARACTERÍSTICASERECURSOSDOGROOVY223

FutebolistajogadorLoko=//obtendooFutebolista
jogadorLoko.setValorPasse(50000000)
doublepasseDoLoko=jogadorLoko.getValorPasse()
Caso o desenvolvedor ache melhor usar os getters e
settersdeformaexplícitaemsuasclassesGroovy,podefazê-lo
semproblemaalgum.
Osrecursosapresentadosnestecapítulosãoosuficientespara
o leitor iniciante em Groovy, porém existem ainda muito mais
recursosinteressantesnessalinguagem.Seoleitordesejarumguia
completo, sua documentação oficial (http://groovy-
lang.org/documentation.html) é muito bem escrita e rica em
exemplos.
Conclusão
22413.2CARACTERÍSTICASERECURSOSDOGROOVY