Introspección

Averiguando los Constructores de una Clase

Groovy permite un alto grado de introspección. Por ejemplo, es posible preguntar a una clase por el conjunto de constructores que soporta:

groovy:000> String.constructors.each { println it }
public java.lang.String()
public java.lang.String(java.lang.String)
public java.lang.String(char[])
public java.lang.String(char[],int,int)
public java.lang.String(int[],int,int)
public java.lang.String(byte[],int,int,int)
public java.lang.String(byte[],int)
public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException
public java.lang.String(byte[],int,int,java.nio.charset.Charset)
public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException
public java.lang.String(byte[],java.nio.charset.Charset)
public java.lang.String(byte[],int,int)
public java.lang.String(byte[])
public java.lang.String(java.lang.StringBuffer)
public java.lang.String(java.lang.StringBuilder)
===> [Ljava.lang.reflect.Constructor;@7284aa02

Averiguando las Interfaces de una Clase

Del mismo modo es posible preguntar a una clase por las interfaces que implementa:

groovy:000> String.interfaces.each { println it }   
interface java.io.Serializable
interface java.lang.Comparable
interface java.lang.CharSequence
===> [Ljava.lang.Class;@53e6978d

Campos de un objeto

Es posible también conocer los campos de una clase:

groovy:000> d = new Date()
===> Fri Apr 23 16:34:38 BST 2010
groovy:000> println d.properties
[time:1272036878419, date:23, timezoneOffset:-60, hours:16, seconds:38, month:3, 
 day:5, calendarDate:2010-04-23T16:34:38.419+0100, 
 class:class java.util.Date, 
 timeImpl:1272036878419, 
 minutes:34, 
 year:110, 
 julianCalendar:sun.util.calendar.JulianCalendar@15535a58]
===> null
El método getProperties de Groovy mira los getters y setters de la clase. En este ejemplo los campos day, month y year no son auténticos campos pero la llamada a getProperties los presenta como tales.

El Campo metaClass

Cuando ejecutamos este programa:

~/Lgroovy/mop$ cat -n person.groovy 
     1  class Person {
     2    String firstName
     3    String secondName
     4  }
     5  
     6  def p = new Person(firstName: 'Juana', secondName: 'Rodriguez')
     7  p.properties.each { println it }
Obtenemos una salida como esta:
~/Lgroovy/mop$ groovy Person
firstName=Juana
secondName=Rodriguez
class=class Person
metaClass=org.codehaus.groovy.runtime.HandleMetaClass@77bf7b23[groovy.lang.MetaClassImpl@77bf7b23[class Person]]
Además de los campos firstName y secondName aparecen dos campos: class y metaClass.

El método getProperties() retorna todos los campos de un objeto, incluyendo los heredados. La clase Person hereda de Object y por tanto dispone de los campos class y metaClass comunes a todos los objetos.

El método hasProperty

La metaclase de una clase dada provee métodos para la introspección y modificación de la clase. Por ejemplo, el método hasProperty de la metaclase permite consultar si una clase dispone de un cierto atributo. Retorna null si la clase no dispone de esa propiedad.

He aquí un ejemplo:

~/Lgroovy/mop$ cat -n hasProperty.groovy 
 1  #!/usr/bin/env groovy
 2  
 3  class Person {
 4    String firstName
 5    String secondName
 6  }
 7  
 8  def p = new Person()
 9  
10  if (args.size() < 2) {
11    System.err.println "Error. Provide two arguments"
12    System.exit(1)
13  }
14  
15  if (p.metaClass.hasProperty(p, args[0])) {
16    p[args[0]] = args[1]
17  }
18  else {
19    System.err.println "Error. Field '${args[0]}' does not exists"
20    System.exit(2)
21  }
22  
23  println (p[args[0]])
Siguen varias ejecuciones:
~/Lgroovy/mop$ ./hasProperty.groovy firstName Juana
Juana
~/Lgroovy/mop$ ./hasProperty.groovy firstNam Juana
Error. Field 'firstNam' does not exists
~/Lgroovy/mop$ ./hasProperty.groovy 
Error. Provide two arguments
~/Lgroovy/mop$ ./hasProperty.groovy secondName Rodriguez
Rodriguez

También podríamos haber usado el método setProperty para asignar un valor al atributo:

~/Lgroovy/mop$ diff hasProperty.groovy hasProperty2.groovy 
16c16
<   p[args[0]] = args[1]
---
>   p.setProperty(args[0], args[1])
~/Lgroovy/mop$ ./hasProperty2.groovy secondName Rodriguez
Rodriguez

Métodos de una Clase

Consultaremos el atributo methods del atributo class o bien al método getMethods() del atributo class para obtener la lista de métodos de un objeto:

groovy:000> d = new Date()
===> Sat Apr 24 15:14:33 BST 2010
groovy:000> d.class.methods.each { println it }
public int java.util.Date.hashCode()
public java.lang.Object java.util.Date.clone()
public boolean java.util.Date.equals(java.lang.Object)
public int java.util.Date.compareTo(java.util.Date)
public int java.util.Date.compareTo(java.lang.Object)
public java.lang.String java.util.Date.toString()
public boolean java.util.Date.after(java.util.Date)
public boolean java.util.Date.before(java.util.Date)
public static long java.util.Date.parse(java.lang.String)
public void java.util.Date.setTime(long)
public long java.util.Date.getTime()
public int java.util.Date.getYear()
public int java.util.Date.getMonth()
public int java.util.Date.getDate()
public int java.util.Date.getHours()
public int java.util.Date.getMinutes()
public int java.util.Date.getSeconds()
public static long java.util.Date.UTC(int,int,int,int,int,int)
public void java.util.Date.setDate(int)
public void java.util.Date.setMonth(int)
public void java.util.Date.setHours(int)
public void java.util.Date.setMinutes(int)
public void java.util.Date.setSeconds(int)
public void java.util.Date.setYear(int)
public int java.util.Date.getDay()
public java.lang.String java.util.Date.toLocaleString()
public java.lang.String java.util.Date.toGMTString()
public int java.util.Date.getTimezoneOffset()
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
===> [Ljava.lang.reflect.Method;@5c0e920f
También podemos consultar simplemente el nombre:
groovy:000> d.class.methods.name.each { println it }
hashCode
clone
equals
compareTo
compareTo
toString
after
before
parse
setTime
getTime
getYear
getMonth
getDate
getHours
getMinutes
getSeconds
UTC
setDate
setMonth
setHours
setMinutes
setSeconds
setYear
getDay
toLocaleString
toGMTString
getTimezoneOffset
wait
wait
wait
getClass
notify
notifyAll

Llamando Dinámicamente a un Método Usando evaluate

Una forma de llamar un método cuyo nombre no se conoce hasta el momento de la ejecución es usar evaluate (Véase la sección 1.9). El siguiente ejemplo ilustra la técnica:

~/Lgroovy/mop$ cat -n ./evaluateMethods.groovy 
     1  #!/usr/bin/env groovy
     2  def p = new Date()
     3  def b = new Binding()
     4  def gs = new GroovyShell(b)
     5  
     6  b.setVariable("d", p)
     7  p.class.methods.each { method ->
     8    if (method.name.startsWith("get")) {
     9        print "${method.name}:"
    10        r = gs.evaluate("d.${method.name}()")
    11        println r
    12    }
    13  }
Sigue un ejemplo de ejecución:
~/Lgroovy/mop$ ./evaluateMethods.groovy 
getTime:1272120130791
getYear:110
getMonth:3
getDate:24
getHours:15
getMinutes:42
getSeconds:10
getDay:6
getTimezoneOffset:-60
getClass:class java.util.Date

Llamando Dinámicamente a un Método Usando Referenciado Simbólico

El operador dot permite usar una cadena de doble comilla como segundo argumento:

~/Lgroovy/mop$ cat -n symbolicCalling.groovy 
     1  #!/usr/bin/env groovy
     2  def p = new Date()
     3  p.class.methods.each { method ->
     4    if (method.name.startsWith("get")) {
     5        print "${method.name}:"
     6        println p."${method.name}"()
     7    }
     8  }
Ejecución:
~/Lgroovy/mop$ ./symbolicCalling.groovy 
getTime:1272120635727
getYear:110
getMonth:3
getDate:24
getHours:15
getMinutes:50
getSeconds:35
getDay:6
getTimezoneOffset:-60
getClass:class java.util.Date

Que Métodos tiene una Clase

Cuando ejecutamos este programa:

~/Lgroovy/mop$ cat -n whatMethods.groovy 
     1  #!/usr/bin/env groovy
     2  
     3  class Person {
     4    String firstName
     5    String secondName
     6  }
     7  
     8  def p = new Person()
     9  
    10  println p.class.methods.name
Vemos que ademas de los getters y setters:
~/Lgroovy/mop$ groovy whatMethods.groovy 
getFirstName, setFirstName, getSecondName, setSecondName,
tenemos métodos de java.lang.Object y java.lang.Class como:
super$1$hashCode, super$1$finalize, 
super$1$notifyAll, super$1$getClass, super$1$equals, super$1$clone, 
super$1$wait, super$1$wait, super$1$wait, super$1$toString, super$1$notify,

y métodos de groovylang.GroovyObject:

setProperty, getProperty, invokeMethod, getMetaClass, setMetaClass, 
wait, wait, wait, hashCode, 
getClass, equals, toString, notify, notifyAll

Comprobando la existencia de un Método

Podemos utilizar el método respondsTo de la metaClass para verificar la existencia de un método en la clase. El siguiente guión llama al método especificado como primer argumento en la línea de comandos en el caso de que este tenga una implementación en una cierta clase:

~/Lgroovy/mop$ cat -n respondsTo.groovy 
     1  class Myshell {
     2    def ls() {
     3      'ls'.execute()
     4    }
     5  
     6    def ls(args) {
     7      (['ls']+args).execute()
     8    }
     9  }
    10  
    11  def p = new Myshell()
    12  
    13  if (args.size() < 1) System.exit(1)
    14  
    15  command = args[0]
    16  if (p.metaClass.respondsTo(p, command)) {
    17    switch (args.size()) {
    18      case 1:  println p."$command"().text
    19               break
    20      default: println p."$command"(args[1..-1]).text
    21    }
    22  }
    23  else {
    24    println "Unknown command '$command'"
    25  }
En caso de ambiguedad, si nuestra clase dispone de varias versiones sobrecargadas del método le pasaremos a respondsTo parámetros que especifiquen la firma. Le pasaremos un null si queremos especificar que no acepta argumentos. Por ejemplo:
if (p.metaClass.respondsTo(p, "ls", String, String) {
  p.ls("-l", "-t")
}

Siguen varias ejecuciones:

~/Lgroovy/mop$ groovy respondsTo.groovy chuchu
Unknown command 'chuchu'
~/Lgroovy/mop$ groovy respondsTo.groovy ls
ExamineMetaClass.groovy
InterceptingInteger.groovy
InterceptingIntegerViaMetaClass.groovy
evaluateMethods.groovy
hasProperty.groovy
hasProperty2.groovy
methodcalls
person.groovy
respondsTo.groovy
symbolicCalling.groovy
symbolicCalling2.groovy
whatMethods.groovy

~/Lgroovy/mop$ groovy respondsTo.groovy ls -l
total 88
-rw-r--r--  1 casianorodriguezleon  staff   42 26 feb 07:34 ExamineMetaClass.groovy
-rw-r--r--  1 casianorodriguezleon  staff  385 15 abr 05:44 InterceptingInteger.groovy
-rwxr-xr-x  1 casianorodriguezleon  staff  530 23 abr 16:09 InterceptingIntegerViaMetaClass.groovy
-rwxr-xr-x  1 casianorodriguezleon  staff  283 24 abr 15:39 evaluateMethods.groovy
-rwxr-xr-x  1 casianorodriguezleon  staff  379 24 abr 13:52 hasProperty.groovy
-rwxr-xr-x  1 casianorodriguezleon  staff  390 24 abr 15:11 hasProperty2.groovy
drwxr-xr-x  5 casianorodriguezleon  staff  170 15 abr 05:44 methodcalls
-rw-r--r--  1 casianorodriguezleon  staff  155 23 abr 16:58 person.groovy
-rw-r--r--  1 casianorodriguezleon  staff  416 30 abr 07:00 respondsTo.groovy
-rwxr-xr-x  1 casianorodriguezleon  staff  185 24 abr 15:50 symbolicCalling.groovy
-rwxr-xr-x  1 casianorodriguezleon  staff  185 24 abr 15:55 symbolicCalling2.groovy
-rwxr-xr-x  1 casianorodriguezleon  staff  131 30 abr 06:08 whatMethods.groovy

~/Lgroovy/mop$ groovy respondsTo.groovy ls -l -t -r
total 88
-rw-r--r--  1 casianorodriguezleon  staff   42 26 feb 07:34 ExamineMetaClass.groovy
drwxr-xr-x  5 casianorodriguezleon  staff  170 15 abr 05:44 methodcalls
-rw-r--r--  1 casianorodriguezleon  staff  385 15 abr 05:44 InterceptingInteger.groovy
-rwxr-xr-x  1 casianorodriguezleon  staff  530 23 abr 16:09 InterceptingIntegerViaMetaClass.groovy
-rw-r--r--  1 casianorodriguezleon  staff  155 23 abr 16:58 person.groovy
-rwxr-xr-x  1 casianorodriguezleon  staff  379 24 abr 13:52 hasProperty.groovy
-rwxr-xr-x  1 casianorodriguezleon  staff  390 24 abr 15:11 hasProperty2.groovy
-rwxr-xr-x  1 casianorodriguezleon  staff  283 24 abr 15:39 evaluateMethods.groovy
-rwxr-xr-x  1 casianorodriguezleon  staff  185 24 abr 15:50 symbolicCalling.groovy
-rwxr-xr-x  1 casianorodriguezleon  staff  185 24 abr 15:55 symbolicCalling2.groovy
-rwxr-xr-x  1 casianorodriguezleon  staff  131 30 abr 06:08 whatMethods.groovy
-rw-r--r--  1 casianorodriguezleon  staff  416 30 abr 07:00 respondsTo.groovy



Subsecciones
Casiano Rodríguez León
2010-04-30