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
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
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] ===> nullEl 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.
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.
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
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;@5c0e920fTambié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
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
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
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.nameVemos 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
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