EXPLORANDO GROOVY, PARTE II
Para el programador proveniente de Java, que en la actualidad debe ser un porcentaje muy alto de los nuevos programadores de Groovy, hay ciertas cosas que saltan a la vista y generan preguntas interesantes sobre cómo se trasladan muchos de los conceptos de Java a Groovy. Primero hay que tener en cuenta que si bien Groovy está pensado para ser fácil de aprender para los programadores da Java, es un lenguaje distinto con filosofía distinta; al mismo tiempo que es mucho mas orientado a objetos que Java, incorpora programación funcional por medio del uso intensivo de closures y abrasa conceptos de metaprogramming y domain specific languages, lo cual lo hace un lenguaje ideal para el desarrollo de herramientas y aplicaciones de dominios complejos.
En esta entrega quiero tratar de explicar un poco mejor como resuelve el Groovy los ámbitos de las variables y como se diferencia de Java. Esta área del Groovy ha sufrido varios cambios importantes desde sus primeras versiones, así que para todos los ejemplos que voy a mostrar a continuación asumo que estamos empleando Groovy 1.8.
En el artículo anterior omití hablar de algunas variables automáticas que crea Groovy en distintos contextos, en la misma manera que Java genera this y super, Groovy ademas de estas tiene it y owner entre otros. El it lo vimos en el pasado y es el argumento de un closure en el caso que este solo tenga uno. El owner por otra parte representa el contexto donde se ejecuta el closure, que para el caso de los closures no anidados en el objeto mismo, pero para los anidados es el closure padre. Otra cosa de la que olvidé hablar es cómo los string de groovy soportan tanto comillas sencillas como dobles para sus literales, y cuando usan comillas dobles podemos usar expresiones tipo EL para la inclusión de valores
Bueno a continuación un ejemplo en el que trato de explorar un poco estos conceptos.
// scopeTests.groovy package tests // class Klass { Closure createClosure() { return { def list = [it, owner, this] // creamos un closure anidado que es el que retornamos def nested = { list + [it, owner, this]} return nested } } } klass = new Klass() closure = klass.createClosure() // creamos un closure nested = closure(this) // iniciamos el closure anidado list1 = nested('arg') // ejecutamos el closure anidado list2 = closure(this)('arg') // list1 y list2 son equivalentes println "list1 == list2 : ${list1 == list2} : $list1" // note como 'it' dentro del contexto del closure externo tiene el valor // del argumento de dicho closure, que en este caso es 'this' desde el ámbito // del scrip. Y ya que groovy genera un objeto que representa al archivo, es // decir que el archivo es una definición de clase si esta no es explícita println "list1[0].class.name == this.class.name : " + "${list1[0].class.name == this.class.name} : ${list1[0].class.name}" // 'it' dentro del contexto del closure anidado tiene el valor // del argumento de dicho closure 'arg' println "list1[3] == 'arg' : ${list1[3] == 'arg'} : ${list1[3]}" // en el contexto del closure externo 'owner' es igual a 'this' println "list1[1].class.name == list1[2].class.name : " + "${list1[1].class.name == list1[2].class.name} : ${list1[2].class.name}" // 'this' simpre se refiere al objeto padre sin importar que estemos en un // closure anidado println "list1[2].class.name == list1[5].class.name : " + "${list1[2].class.name == list1[5].class.name} : ${list1[5].class.name}" // sin embargo 'owner' es diferente dependiendo del closure desde donde estemos // y su nivel de anidamiento println "list1[1].class.name != list1[4].class.name : " + "${list1[1].class.name != list1[4].class.name} + : " + "${list1[1].class.name} : ${list1[4].class.name}"