Detecting Class Innards in Groovy

When using a new language or using new features of a language that I have not used before, I like to know what fields and methods are supported by various classes and objects in that language. This has certainly been the case as I have learned and used Groovy. In this post, I look at the many ways one can find out what a Groovy class/object has to offer.

Javadoc API Documentation

One of the things I have always liked about Java is the ready availability of its standard API documentation. Fortunately, Groovy's GDK extensions and the Groovy distribution classes are similarly well documented. I find myself frequently using these generated HTML pages to discover new APIs that are available to me in the standard Groovy distribution.

Object.toString()

Groovy inherits positive Java features including the ability for object's to easily provide their state via a standard approach by explicitly overriding Object.toString(). This is a valuable tactic in Java (see Item 9 in Effective Java) and continues to be valuable in Groovy. The most significant downside of the toString() approach is that its value relies completely on the author of the class. There are several things a developer can do to make more effective toString() methods, but there's no guarantee any of those things have been done.

In fact, there's no guarantee that a toString() metho for a given class whose instance is being used even provides an explicit overridden toString() method. The next code listing is of a simple Groovy class that fails to explicitly override toString().

/**
 * Simple Groovy class used in demonstration of Groovy detecting methods without
 * explicitly overridden inspect() method, toString() method, or dump() method.
 *
 * @author Dustin
 */
class Person
{
   String lastName

   String firstName

   Person(final String newLastName, final String newFirstName)
   {
      this.lastName = newLastName
      this.firstName = newFirstName
   }
}

When an instance of this simple Groovy class has it's toString() invoked (whether implicitly or explicitly), the output is the same as for a Java class with no explicit toString() (and as defined by Object.toString() Javadoc). It will look something like this:

Person@149eb9f

Object.inspect()

Groovy's GDK Object class specifies an inspect() method for which the Javadoc states: "Inspects returns the String that matches what would be typed into a terminal to create this object." I rarely find this method very useful, but do cover it briefly here.

The inspect() method is often not implemented. When this is the case, the same object's toString() method is invoked. If the object's toString() method is not explicitly implemented, Java's Object.toString() is invoked.

This is demonstrated in the next screen snapshots for the Java Date class and for the Groovy AntBuilder class (the latter of which does not have an explicit inspect() or an explicit toString() implementation).

The Groovy Object.inspect() method suffers the same weakness as Java's (and Groovy's) Object.toString(): the method is only as good as what the author of the class implemented (which might be nothing). In the worst case, an author might not have overridden the inspect() method or the toString() method (as was the case for AntBuilder in the example above) and then very little information is provided.

The next code listing is for a simple Groovy class that implements toString() but fails to implement inspect().

PersonWithToString.groovy

/**
 * Simple Groovy class used in demonstration of Groovy detecting methods without
 * explicitly overridden inspect() method, but with an explicitly overridden
 * toString() method.
 *
 * @author Dustin
 */
class PersonWithToString
{
   String lastName

   String firstName

   PersonWithToString(final String newLastName, final String newFirstName)
   {
      this.lastName = newLastName
      this.firstName = newFirstName
   }

   @Override
   String toString()
   {
      return this.firstName + " " + this.lastName;
   }
}

If the above class is instantiated, it will return the same String whether toString() is explicitly or implicitly called or if inspect() is called. For example, suppose the class is instantiated and invoked as shown in the next code listing.

def person2 = new PersonWithToString("Rubble", "Barney")
doToString(person2)
doInspect(person2)

def doToString(obj)
{
   println "toString(): ${obj}\n"
}

def doInspect(obj)
{
   // Inspect
   println "inspect(): ${obj.inspect()}\n"
}

The output when the above is executed looks like this:

toString(): Barney Rubble

inspect(): Barney Rubble

We can explicitly override inspect() to make it useful and to meet its advertised reason for existence. An example of how this might be done is demonstrated in the next code listing.

PersonWithInspect.groovy

/**
 * Simple Groovy class used in demonstration of Groovy detecting methods with
 * explicitly overridden inspect() method but without explicitly overridden
 * dump() method.
 *
 * @author Dustin
 */
class PersonWithInspect
{
   String lastName

   String firstName

   PersonWithInspect(final String newLastName, final String newFirstName)
   {
      this.lastName = newLastName
      this.firstName = newFirstName
   }

   @Override
   String toString()
   {
      return this.firstName + " " + this.lastName
   }

   @Override
   String inspect()
   {
      return "new Person(" + this.lastName + ", " + this.firstName + ")"
   }
}

When this code has its toString() and inspect() methods called similarly to how the last Groovy class's methods were called, the output is now different:

toString(): Barney Rubble

inspect(): new Person(Rubble, Barney)

Overriding the inspect() method made it more useful and made it so that it not only provides more/different information than already available via toString(), but also helped it achieve the declared (in comments) reason for its existence: to "return the String that matches what would be typed into a terminal to create this object."

Object.dump()

The Object.dump() method often provides more details than Object.inspect(). Its Javadoc documentation states about this method: "Generates a detailed dump string of an object showing its class, hashCode and fields."

The next screen snapshot shows what the dump method displays for a new Java Date instance.

The next screen snapshot shows the AntBuilder.dump() results, which are far more descriptive than the AntBuilder.inspect() method provided.

The Object.dump() method works even when the class whose instance it is being invoked against has not even overridden the dump method. For example, suppose that the Groovy class PersonWithInspect used above had dump() called against it. Even though that class never explicitly defines such a method, calling it leads to results like this:

<PersonWithInspect@1bbd7b2 lastName=Rubble firstName=Barney>

Although no dump() method was explicitly written, calling dump() worked and returned the name of the class with its hash code and with its two attributes (lastName and firstName).

A significant advantage of Groovy's Object.dump() is that it does provide more details about an object's current state without the class author implementing or overriding any specific methods. A Groovy developer is still free to override dump() if so desired, but this is probably rarely needed or even recommended because the existing dump() method is a reasonable implementation.

The next code listing provides a simple Groovy class in which the author has chosen (perhaps poorly) to explicitly override Object.dump().

PersonWithDump.groovy

/**
 * Simple Groovy class used in demonstration of Groovy detecting methods with
 * explicitly overridden dump() method. This is typically not necessary or even
 * desirable as the dump() method is standardly supported.
 *
 * @author Dustin
 */
class PersonWithDump
{
   String lastName

   String firstName

   PersonWithDump(final String newLastName, final String newFirstName)
   {
      this.lastName = newLastName
      this.firstName = newFirstName
   }

   @Override
   String toString()
   {
      return this.firstName + " " + this.lastName
   }

   @Override
   String inspect()
   {
      return "new Person(" + this.lastName + ", " + this.firstName + ")"
   }

   @Override
   String dump()
   {
      return toString()
   }
}

When an instantiation of the above Groovy class has its toString(), inspect(), and dump() methods called, the output appears as follows.

toString(): Barney Rubble

inspect(): new Person(Rubble, Barney)

dump(): Barney Rubble

The above example demonstrates that dump() can be overridden if desired. In this exmaple, it was overridden to simply provide the result of the toString(), which is obviously less information than the default dump() implementation provides.

Before leaving discussion of the Groovy's dump() method, one might ask oneself, "How, or even does, it work for instances of Java classes used in a Groovy context?" That's a great question. The answer to it begins with the next code listing, which shows a small piece of code that instantiates a Java String, queries it for its class and attempts to dump its contents.

def string = "Dustin was here."
println string.class
doDump(string)

def doDump(obj)
{
   println "dump(): ${obj.dump()}\n"
}

The output when the above code is executed is shown next.

class java.lang.String
dump(): <java.lang.String@796944de value=Dustin was here. offset=0 count=16 hash=2036942046>

Nice! Even the instantiated class, which is unsurprisingly treated as a Java String, has support for the Groovy-provided dump() method. That is the beauty or black magic of Groovy's dynamic metadata support in action.

Direct Reflection

So far, I've looked at detection of what a particular Groovy class has to offer via some ways that are standard to Java as well (Javadoc documentation and Object.toString()) and

have also looked at two approaches that are specific to Groovy (Object.inspect() and Object.dump()). Another way to inspect a particular object in Groovy is to use Java's reflection capabilities. Groovy makes it a little easier to use Java's reflection capabilities directly. This is a powerful technique that can require more effort than some of the other approaches, but can be used programatically and can be used to find a plethora of details about a given object's underlying class, method, and attribute structure.

The next code listing demonstrates access of a Groovy class's name, methods names, and fields names via direct Java reflection on the instance of class PersonWithDump shown above.

def doReflection(obj)
{
   println("Reflection:")
   println "\tClass Name: ${obj.class.name}"
   def methods = obj.class.declaredMethods
   def methodsNames = new StringBuilder()
   methods.each
   {
      methodsNames << it.name << " "
   }
   println "\tMethods Names: ${methodsNames}"
   def fields = obj.class.declaredFields
   def fieldsNames = new StringBuilder()
   fields.each
   {
      fieldsNames << it.name << " "
   }
   println "\tFields Names: ${fieldsNames}"
}

When the above reflection-based code is executed against an instance of PersonWithDump, the output appears as shown next.

Reflection:
 Class Name: PersonWithDump
 Methods Names: invokeMethod getMetaClass setMetaClass inspect $getCallSiteArray $createCallSiteArray class$ $getStaticMetaClass $get$$class$groovy$lang$MetaClass this$dist$invoke$2 $get$$class$java$lang$String this$dist$set$2 this$dist$get$2 super$1$wait super$1$wait super$1$wait super$1$toString super$1$notify super$1$notifyAll super$1$getClass super$1$equals super$1$clone super$1$hashCode super$1$finalize getLastName setLastName getFirstName setFirstName $get$$class$PersonWithDump setProperty getProperty toString dump 
 Fields Names: lastName firstName $staticClassInfo metaClass __timeStamp __timeStamp__239_neverHappen1295139333183 $callSiteArray $class$groovy$lang$MetaClass $class$java$lang$String $class$PersonWithDump 

This example has shown that reflection can be easily applied directly in Groovy as it is applied in Java. In fact, Groovy's use of Java reflection even supports the identification of internal Groovy-only metadata methods and fields.

Object.getProperties()

Groovy offers an additional convenience method for determining information on a Groovy class's properties. The output shown next is what one can expect to see when an instance has its getProperties() method called either by calling the method explicitly or using Groovy's syntactic sugar to access it simply with .properties.

[class:class PersonWithDump, firstName:Barney, lastName:Rubble, metaClass:org.codehaus.groovy.runtime.HandleMetaClass@14dd758[groovy.lang.MetaClassImpl@14dd758[class PersonWithDump]]]

Note that no getProperties() method was explicitly provided in the PersonWithDump class, but calling person4.properties still leads to the properties information being made available in name/value pairs. Like Object.dump(), Object.getProperties() provides useful details without needing to specifically implement/override that method.

Does this nice Groovy getProperties() approach work for Java classes used in Groovy? To find out, we start with this code listing for a simple Java class called JavaPerson.

JavaPerson.java

Related:
1 2 3 Page 1
Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.