Closures

O que é uma closure?

Se há um ponto em Groovy que devemos nos aprofundar, este tópico consiste na descrição das closures (aliás, seriam “as closures” ou “os closures”?). Para aqueles que programam em linguagens que não oferecem este recurso, a seguinte metáfora descreve bem o que vêm a ser esta criatura:

Podemos pensar em nossas variáveis como envelopes que armazenam informações. Ao abrir este envelope, encontramos uma string, um valor numérico, um objeto, enfim: um dado.
Uma closure por sua vez consiste em um tipo diferente de variável. Seguindo a metáfora, nosso envelope conteria não uma informação, mas sim um conjunto de instruções, resumindo: código executável.

Como vocês perceberão daqui para frente: closures consistem em um daqueles recursos que, uma vez aprendido, ficamos impressionados com o fato de havermos conseguido programar tanto tempo sem utilizá-lo.

Como declarar uma closure

Como mencionado, uma closure consiste em mais um tipo de variável e, como tal, é declarado como uma variável, tal como no exemplo abaixo:


class ClasseBoba {

// bla bla bla pra cima

def closure = {

println "Sou uma closure"

}

// bla bla bla pra baixo

}

Como pode ser percebido, uma closure é definida quase que exatamente qualquer variável que conhecemos. A única diferença está na sintaxe. Ao lado direito da declaração, ao invés de incluirmos um valor, estamos incluindo um bloco de código executável. No caso, a closure ao ser invocada irá imprimir no console o texto “Sou uma closure”

Closures, assim como qualquer método, também possuem parametros. Caso nenhum parametro seja declarado (como no exemplo acima), estará disponível no bloco de código da closure uma variável implícita chamada it. Podemos portanto incrementar o exemplo tal como farei abaixo:


class ClasseBoba {

// bla bla bla pra cima

def closure = {
println "Sou uma closure que recebeu como parâmetro o valor de ${it}"
}

// bla bla bla pra baixo

}

Caso nenhum parâmetro seja passado para a closure, ela irá imprimir algo como


Sou uma closure que recebeu como parâmetro o valor de NULL.

Já o código abaixo, imprimiria de outra maneira:


def instancia = new ClasseBoba()
instancia.closure("qualquer coisa")

// Será impresso aqui exatamente
//"Sou uma closure que recebeu como parâmetro o valor qualquer coisa"

Óbviamente, uma closure pode receber também mais de um parâmetro, tal como exposto no exemplo abaixo:


def soma = {a, b ->
a + b}

Como pode ser visto no exemplo: se quisermos declarar os parâmetros de uma closure, estes estarão declarados no início do bloco. Finalizada a declaração dos atributos (você pode definir os tipos dos parâmetros se quiser também), segue a sequencia de caracteres “->”, que indica o início do bloco de código em questão.

No caso deste exemplo, criamos uma closure que faz a soma de duas variáveis.

Também é possível modificar o nome do parâmetro de uma closure, tal como no exemplo abaixo:


def closureBoba = {parametro ->
println """Como pode ser visto, o parametro único não precisa se chamar it.
Na realidade, pode ter o nome que você quiser.
A propósito, recebi o valor de ${parametro}""" }

Como referenciar uma closure

Tal como mencionei no início, uma closure nada mais é do que uma variável. Na realidade, trata-se de uma instância da classe groovy.lang.Closure. Sendo assim, você a referencia da mesma maneira que faria com qualquer atributo/variável convencional, tal como no exemplo abaixo:


class Classe1 {
def closureClasse1
}

class Classe2 {
def closureClasse2
}

def closure = {
println "Sou uma closure bem promíscua"
}

Classe1 classe1 = new Classe1()
Classe2 classe2 = new Classe2()

classe1.closure = closure

classe2.closure = classe1.closure

O exemplo faz o seguinte: primeiro define duas classes (Classe1 e Classe2) e, em seguida, implementa uma closure bem simples.

Instanciadas tanto classe1 quanto classe2, primeiro definimos que a closure de classe1 será closure. Em seguida, definimos que a closure de classe2 será a mesma closure de classe1.

Perceberam que bacana? Com closures, podemos mudar o comportamento de nossas classes de uma maneira incrívelmente simples!

Como executar uma closure

A execução de uma closure é exatamente igual a execução de qualquer outro método Groovy, tal como pode ser visto no exemplo abaixo:


def soma = {a, b -> a + b}

def closureSimples = {"Sou simples demais"}

def closureOla = {"Olá ${it}"}

soma(3, 4) // imprimirá 7

closureSimples() // imprime "Sou simples demais"

closureOla("Kico") // imprime "Olá Kico"

Os parênteses são obrigatórios na execução de uma closure.

Como executar uma closure a partir de código Java

Como mencionei anteriormente, uma closure é na realidade um objeto: uma instância da classe groovy.lang.Closure.

Sendo assim, em Java poderiamos executar uma closure tal como no exemplo abaixo:


// O código da closure

def soma = {a, b -> a + b}

// O código Java

Closure closure = soma

soma.call({4,3}) // basta passar uma matriz representando os parametros necessários!

Escopo de execução

Veja o código abaixo:


class QualquerClasse {

public void metodoQualquer() {

int x = 10

def closure = {

println "O valor de X é ${x}"

}

closure()

}

}

Como a closure definida em metodoQualquer() sabe da existência da variável x? Devido ao seu escopo. Uma closure possui acesso a todos os atributos definidos no bloco de código em que a mesma é definida.

Paralelamente, o método aonde a closure é definida não possui acesso às suas variáveis internas. O código abaixo por exemplo dispararia uma excessão:


class ClasseFurada {

public void metodoFurado() {

int x = 10;

def closure = {
int y = 1979

println "Não é injusto? Eu vejo ${x}, mas voce não verá minhas variaveis jamais!"}

closure()
print closure.y // ERRO!

}

}

6 thoughts on “Closures

  1. Olá,

    Muito bom, estou iniciando e cada vez gosto mais deste grails/gloovy… Seu artigo está muito bom e estas closures são bem boladas… mesmo lendo mais de um ano do post. :-)

    Mas os nomes devem sempre iniciar em closure ?

    closureClasse1 é o mesmo que Classe1.closure ?

    []’s Everton

    Responda

    admin Reply:

    Oi Everton. Fico feliz que tenha gostado, mas não entendi a sua pergunta. Como assim?

    Responda

    Rodrigo Maia Reply:

    Kiko,

    acho que entendi a dúvida dele,

    class Classe1 {
    def closureClasse1
    }

    Tu declarou closureClasse1 e mais na frente:

    classe1.closure = closure

    mas tu queria escrever assim:

    classe1.closureClasse1 = closure

    Responda

    Augusto Reply:

    Acredito que seja a mesma duvida que a minha.
    na declaração das classes os nomes das “closures” eram
    closureClass1 e closureClass2.
    Porém no momento de definir qual é o funcionamento/comportamento da closure foi apresentado
    classe1.closure = closure
    classe2.closure = classe1.closure
    mas na definição das classe Classe1 e 2 não exitia o atributo closure

    Responda

    Augusto Reply:

    para complementar a minha duvida é se não declarando as propriedades closure nas classes 1 e 2 essa atribuição funcionaria (criando) essa propriedade ou da forma que foi descrito darria erro?

    Responda

    Kico (Henrique Lobo Weissmann) Reply:

    Augusto, rola de postar esta dúvida no Grails Brasil?
    Aí fica mais fácil te responder e a gente ainda ajuda outras pessoas que também podem estar com a mesma dúvida. :)

    Responda

Leave a Reply

Your email address will not be published. Required fields are marked *