In this first recipe about metaprogramming, we begin by looking at the introspection capabilities of Groovy. Introspection is a major feature of the Java language and, by extension, of the Groovy language. Using introspection, we can get the internal information of a class at runtime, including fields, methods, constructors, and so on.
To test out the introspection feature, let's create a couple of Groovy classes:
package org.groovy.cookbook class Book { String title Author author Long year Long pages Long getAmazonSalesPosition() { new Random().nextInt(1) + 1 } void attachReview(String review) { } } class Author { String name String lastName }
The following steps offer an example of the introspection capabilities of the language:
assert 'java.lang.String' == String.name assert 'org.groovy.cookbook.Author' == Author.name
Author a = new Author(name: 'Ernest', lastName: 'Hemingway') Book book = new Book() book.with { title = 'The Old Man and the Sea' year = 1952 pages = 200 author = a } book.properties.each { println it }
The output of looping on the instance's properties is as follows:
title=The Old Man and the Sea class=class org.groovy.cookbook.Book amazonSalesPosition=1 year=1952 pages=200 metaClass= org.codehaus.groovy.runtime.HandleMetaClass @41ce774e[groovy.lang.MetaClassImpl@41ce774e[ class org.groovy.cookbook.Book]] author=org.groovy.cookbook.Author@59fac3a2
metaClass
. Through this property, we can access quite many interesting methods to figure out what our class can do. For instance, to check the existence of a property, we can use the following code snippet:assert book.metaClass.hasProperty(book, 'pages')
println '#### METHODS ####' book.metaClass.methods.each { println it } println '#### PROPERTIES ####' book.metaClass.properties.each { println it.name }
Note how the methods' property prints all the methods inherited from Java (such as equals and hashCode) as well as the local methods of the class:
### METHODS #### public boolean java.lang.Object.equals(java.lang.Object) public final native java.lang.Class java.lang.Object.getClass() public native int java.lang.Object.hashCode() ... public void Book.attachReview(java.lang.String) public java.lang.Long Book.getAmazonSalesPosition() public Author Book.getAuthor() public groovy.lang.MetaClass Book.getMetaClass() public java.lang.Long Book.getPages() ... ### PROPERTIES ### title class amazonSalesPosition year pages author
metaClass
gives access to the respondsTo
method, to directly interrogate an instance about the presence of a method:assert book.metaClass.respondsTo(book,'getAmazonSalesPosition') assert book.metaClass.respondsTo(book,'attachReview', String)
The respondsTo
method can be directly invoked on any object, as it is also exposed on the Groovy enhanced java.lang.Object
class:
assert book.respondsTo('attachReview', String)
Every class in the class loader has a reference to an object of type metaClass
. This metaClass
maintains the list of all methods and properties of a given class, starting with the bytecode information and adding the additional methods that Groovy knows about by default (DefaultGroovyMethods
). Normally, all instances of a class share the same metaClass
. However, Groovy allows per instance metaclasses, that is, different instances of a class that may refer to different metaclasses.
The first example in Step 1 is nothing more than calling getClass().getName()
on a Java class; therefore, piggybacking on the Java reflection capabilities. The remaining examples are more interesting because they leverage the dynamic properties of the Groovy language through the metaClass
attribute. The respondsTo
method, in particular, is useful when writing a dynamic code; for example, populating an arbitrary object from some data on a file.