We can examine classes in Groovy to find out information in the form of strings.
Examining Classes
To find out a class's name and superclasses:
| Code Block |
|---|
class A{}
assert A.name == 'A'
assert new A().class.name == 'A'
assert A.class.name == 'A' //'class' is optionally used here
class B extends A{}
assert B.name == 'B'
class C extends B{}
def hierarchy= []
def s = C
while(s != null){ hierarchy << s.name; s= s.superclass }
assert hierarchy == [ 'C', 'B', 'A', 'java.lang.Object' ]
|
To examine the interfaces:
| 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
|
We can check if a class is a class or an interface:
| Code Block |
|---|
assert Observer.isInterface() assert ! Observable.isInterface() |
We can examine public fields and their types:
| Code Block |
|---|
class A{
def adyn //if no modifier, field is private
String astr
public apdyn
public String apstr
protected aqdyn
}
interface B1{}
interface B2{}
class B extends A implements B1, B2{
def bdyn
int bint
public bpdyn
public int bpint
protected bqdyn
}
def dets = [] as Set
B.fields.each{ //public fields only
dets << [ it.name, it.type.name ] //name of field and name of type
}
assert dets == [
[ 'apstr', 'java.lang.String' ],
[ 'apdyn', 'java.lang.Object' ],
[ 'bpint', 'int' ],
[ 'bpdyn', 'java.lang.Object' ],
[ '__timeStamp', 'java.lang.Long' ], //added by Groovy
] as Set
|
We can look at a certain field of a class:
| 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
|
We can also look at the constructors and methods of a class:
| 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()'
|
Some code to find out all the getters for a class:
| Code Block |
|---|
getters= {
it.methods.name.findAll{ it =~ /^get[A-Z]/ }.
collect{ it[3].toLowerCase()+it[4..-1] }.join(', ')
}
assert getters( GroovyObject ) == 'metaClass, property'
|
To see all nested classes for a particular class (eg, of Character):
| Code Block |
|---|
assert Character.classes.name as Set == [ 'java.lang.Character$Subset', 'java.lang.Character$UnicodeBlock' ] as Set |
To query a particular nested class (eg, Character.UnicodeBlock):
| 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,
(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
}
|
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 |
|---|
class A{
public value1
protected value2
A( int v ){ value1= v; value2 = v }
}
def a= new A( 100 )
assert A.getField( 'value1' ).get( a ) == 100 //public fields only
try{ A.getField( 'value2' ).get( a ); assert false }
catch(Exception e){ assert e instanceof NoSuchFieldException }
A.getField( 'value1' ).set( a, 350 )
assert a.value1 == 350
|
And we can call methods 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
]
|
To enquire the component type/s of an array:
| 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
}
|
We can create and copy arrays when their component type and size is unknown at compile time:
| Code Block |
|---|
import java.lang.reflect.Array def a1 = [55, 66] as int[] //component type and size unknown at compile time... def a2 = Array.newInstance( a1.class.componentType, a1.size() * 2 ) assert a2.class.componentType == int assert a2.size() == 4 System.arraycopy( a1, 0, a2, 0, a1.size() ) assert a2 as List == [55, 66, 0, 0] as List |
We can create multi-dimensional arrays in a similar way, where component type and array sizes can be unknown at compile time:
| Code Block |
|---|
import java.lang.reflect.Array
//assertion checking code...
assert1D= {x,y->
assert x.size() == y.size()
for( int i: x.size() - 1 ) assert x[ i ] == y[ i ]
}
assert2D= {x,y->
assert x.size() == y.size()
for( int i: x.size() - 1 ){
assert x[ i ].size() == y[ i ].size()
for( int j: x[ i ].size() - 1 ) assert x[ i ][ j ] == y[ i ][ j ]
}
}
//each is a 1-D int array with 3 elts
def a0= new char[ 3 ]
def a1= Array.newInstance( char, 3 )
def a2= Array.newInstance( char, [ 3 ] as int[] )
assert1D( a0, a1 )
assert1D( a0, a2 )
//both are a 2-D 3x4 array of String elts
def b0= new String[3][4]
def b1= Array.newInstance( String, [ 3, 4 ] as int[] )
assert2D( b0, b1 )
//both are a 2-D array of 6 char arrays, with undefined tail dimension
def c0 = new char[6][]
def c1 = Array.newInstance( char[], [ 6 ] as int[] )
assert1D( c0, c1 )
|
We can use set() and get() to copy the contents of one array index to another:
| Code Block |
|---|
import java.lang.reflect.Array def a= [ 12, 78 ] as int[], b= new int[ 4 ] Array.set( b, 0, Array.get( a, 0 ) ) assert b[ 0 ] == 12 |
This tutorial is loosely based on Sun's tutorial on Java Reflection, but using Groovy code instead.