Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

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.