Versions Compared


  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

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'
assert new A() == 'A'
assert == 'A' //'class' is optionally used here

class B extends A{}
assert == 'B'

class C extends B{}
def hierarchy= []
def s = C
while(s != null){ hierarchy <<; 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 << }
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 << }
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 << [, ] //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 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{ } 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 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 =~ /^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 as Set ==
  [ 'java.lang.Character$Subset', 'java.lang.Character$UnicodeBlock' ] as Set

To query a particular nested class (eg, Character.UnicodeBlock):

Code Block{ 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 }
    //...then find its method names...

// help us build a custom-formatted listing of method details
  println """$ ${', ')} ) returns \
$ ${it.exceptionTypes.size()>0?'throws ':''}\
${', ')}"""

We can look at the modifiers of methods and classes:

Code Block
import java.lang.reflect.Modifier{}.each{ println it }
  //use reflection on the reflection classes themselves...

// 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{ [,] }
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;' ],
  k, v -> assert [, ] == 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.