As in Java, we We can examine classes in Groovy to find out information in the form of strings, using the Reflection API.
Examining Classes
To find out a class's name and superclasses:
...
| Code Block |
|---|
interface A1{}
interface A2{}
class A implements A1, A2{}
def interfacesA = [] as Set //use a set because interfaces are unordered
A.interfaces.each{ interfacesA << it.name }
assert interfacesA == [ 'A1', 'A2', 'groovy.lang.GroovyObject' ] as Set
interface B1{}
class B extends A implements B1{}
def interfacesB = [] as Set
B.interfaces.each{ interfacesB << it.name }
assert interfacesB == [ 'B1' ] as Set
//only immediately implemented interfaces are reported
|
...
| Code Block |
|---|
assert Observer.isInterface() assert ! Observable.isInterface() |
Examining Within the Class
We can examine public fields and their types:
...
| Code Block |
|---|
assert Math.fields.name as Set == [ 'E', 'PI' ] as Set
assert Math.class.getField('PI').toString() ==
'public static final double java.lang.Math.PI'
assert Math.class.getField('PI').getDouble() == 3.141592653589793
//we must know the type of the value
|
...
| Code Block |
|---|
assert HashMap.constructors.collect{ it.parameterTypes.name } as Set ==
[ ['int'], [], ['java.util.Map'], ['int', 'float'] ] as Set
GroovyObject.methods.each{ println it }
//to print full details of each method of a class
assert GroovyObject.methods.name as Set ==
[ 'invokeMethod', 'getMetaClass', 'setMetaClass',
'setProperty', 'getProperty' ] as Set
assert GroovyObject.getMethod('getMetaClass').toString() ==
'public abstract groovy.lang.MetaClass groovy.lang.GroovyObject.getMetaClass()'
|
...
| Code Block |
|---|
getters= {
it.methods.name.findAll{ it =~ /^get[A-Z]/ }.
collect{ it[3].toLowerCase()+it[4..-1] }.join(', ')
}
assert getters( GroovyObject ) == 'metaClass, property'
|
...
| Code Block |
|---|
Character.UnicodeBlock.fields.name.each{ println it }
//to list all public constants
|
Reflecting the Reflection classes themselves
We can use reflection on the reflection classes themselves. For example:
| Code Block |
|---|
assert Class.methods[0].class == java.lang.reflect.Method //find the class of any method of any class... java.lang.reflect.Method.methods.each{ println it.name } //...then find its method names... //...to help us build a custom-formatted listing of method details HashMap.class.methods.each{ println """$it.name( ${it.parameterTypes.name.join(', ')} ) returns \ $it.returnType.name " + "${it.exceptionTypes.size()>0?'throws ':''}\ ${it.exceptionTypes.name.join(', ')}""" } |
We can look at the modifiers of methods and classes:
| Code Block |
|---|
import java.lang.reflect.Modifier
Modifier.methods.name.sort{}.each{ println it }
//use reflection on the reflection classes themselves...
//...to help us build a custom-formatted listing of modifier details
[ (ArrayList.getMethod( 'remove', [Object] as Class[] )):
[ 'public' ] as Set,
(Collections.getMethod( 'synchronizedList', [List] as Class[] )):
[ 'public', 'static' ] as Set, [ 'public', 'static' ] as Set,
(Math): [ 'public', 'final' ] as Set, (Math): [ 'public', 'final' ] as Set,
(ClassLoader): [ 'public', 'abstract' ] as Set,
].each{ key, val->
def m= key.modifiers
def mods= [
({Modifier.isPublic(it)}): 'public',
({Modifier.isProtected(it)}): 'protected',
({Modifier.isPrivate(it)}): 'private',
({Modifier.isInterface(it)}): 'interface',
({Modifier.isAbstract(it)}): 'abstract',
({Modifier.isFinal(it)}): 'final',
({Modifier.isStatic(it)}): 'static',
({Modifier.isVolatile(it)}): 'volatile',
({Modifier.isNative(it)}): 'native',
({Modifier.isStrict(it)}): 'strict',
({Modifier.isSynchronized(it)}): 'synchronized',
({Modifier.isTransient(it)}): 'transient',
].collect{ k, v-> k(m)? v: null } as Set
mods.removeAll( [null] )
assert mods == val
}
|
Packages
To see all packages loaded by system:
| Code Block |
|---|
Package.packages.name.each{ println it }
|
Manipulating Objects
When a class is unknown at compile time (eg, we only have a string representation of a class name), we can use reflection to create objects:
| Code Block |
|---|
assert Class.forName("java.util.HashMap").newInstance() == [:]
def constructor = Class.forName("java.util.HashMap").
getConstructor( [ int, float ] as Class[] )
assert constructor.toString() == 'public java.util.HashMap(int,float)'
assert constructor.newInstance( 12, 34.5f ) == [:]
|
We can examine and change public fields for a class refering using a String for the name:
...
| Code Block |
|---|
assert String.getMethod( 'concat', [ String ] as Class[] ). invoke( 'Hello, ', [ 'world!' ] as Object[] ) == 'Hello, world!' |
Working with Arrays
We can examine and manipulate arrays. To enquire the public array fields of a class:
| Code Block |
|---|
class A{
public boolean alive
public int[] codes
public Date[] dates
protected boolean[] states
}
//find all public array fields
def pubFields= new A().class.fields.findAll{ it.type.isArray() }.
collect{ [it.name, it.type.name] }
assert pubFields == [
[ 'codes', '[I' ], //'[I' means array of int
[ 'dates', '[Ljava.util.Date;' ], //means array of object java.util.Date
]
|
...
| Code Block |
|---|
[ (int[]): [ '[I', 'int' ],
(Date[]): [ '[Ljava.util.Date;', 'java.util.Date' ],
(new Date[6].class): [ '[Ljava.util.Date;', 'java.util.Date' ],
//instantiated class
(String[][]): [ '[[Ljava.lang.String;', '[Ljava.lang.String;' ],
].each{
k, v -> assert [ k.name, k.componentType.name ] == v
}
|
Manipulating Arrays
We can create and copy arrays when their component type and size is unknown at compile time:
...
This tutorial is loosely based on Sun's tutorial on Java Reflection, but using Groovy code instead.