What Methods Does My Groovy/Grails Class Have?

| Comments

If you’ve ever wondered what methods a groovy class has available for you to call, all you need to do is ask the metaClass:



["equals", "getClass", "hashCode", "notify", "notifyAll", "toString", "wait", "wait", 
"wait", "charAt", "codePointAt", "codePointBefore", "codePointCount", "compareTo", 
"compareTo", "compareToIgnoreCase", "concat", "contains", "contentEquals", 
"contentEquals", "copyValueOf", "copyValueOf", "endsWith", "equals", 
"equalsIgnoreCase", "format", "format", "getBytes", "getBytes",
 "getBytes", "getChars", "hashCode", "indexOf", "indexOf", "indexOf", 
"indexOf", "intern", "lastIndexOf", "lastIndexOf", "lastIndexOf", "lastIndexOf", 
"length", "matches", "offsetByCodePoints", "regionMatches", "regionMatches", 
"replace", "replace", "replaceAll", "replaceFirst", "split", "split", "startsWith",
 "startsWith", "subSequence", "substring", "substring", "toCharArray", 
"toLowerCase", "toLowerCase", "toString", "toUpperCase", "toUpperCase", 
"trim", "valueOf", "valueOf", "valueOf", "valueOf", "valueOf", "valueOf", 
"valueOf", "valueOf", "valueOf"]

There’s a lot of duplication in there, and the order is random, so I’ll often fix that like this:



["charAt", "codePointAt", "codePointBefore", "codePointCount", "compareTo",
 "compareToIgnoreCase", "concat", "contains", "contentEquals", "copyValueOf", 
"endsWith", "equals", "equalsIgnoreCase", "format", "getBytes", "getChars",
 "getClass", "hashCode", "indexOf", "intern", "lastIndexOf", "length", "matches", 
"notify", "notifyAll", "offsetByCodePoints", "regionMatches", "replace", "replaceAll",
 "replaceFirst", "split", "startsWith", "subSequence", "substring", "toCharArray", 
"toLowerCase", "toString", "toUpperCase", "trim", "valueOf", "wait"]

If you haven’t seen that “” in metaClass.methods.name, that’s known as the Spread Operator and it’s essentially the same thing as saying

"foo".metaClass.methods.collect { method -> method.name }.sort().unique()

If you’re using Grails and want to know what methods are on your Author class, looking at the groovy class file will give you a clue if you know the patterns, but it can be easy to forget what the exact convention is:

class Author {
    String name
    def hasMany = [books: Book]

Just start up a grails console and ask an Author what it can do:

println new Author().metaClass.methods*.name.sort().unique()

This will result in:

["$static_methodMissing", "", "asType", "clearErrors", "create", 
"decodeBase64", "decodeHTML", "decodeJSON", "decodeJavaScript",
"decodeURL", "decodeXML", "encodeAsBase64", "encodeAsHTML",
 "encodeAsJSON", "encodeAsJavaScript", "encodeAsURL", "encodeAsXML", 
"equals", "findAll", "getBooks", "getClass", "getConstraints", "getErrors", 
"getHasMany", "getId", "getLog", "getMetaClass", "getName", "getProperties", 
"getProperty", "getVersion", "hasErrors", "hashCode", "ident", "invokeMethod", 
"methodMissing", "notify", "notifyAll", "setBooks", "setErrors", "setHasMany",
"setId", "setMetaClass", "setName", "setProperties", "setProperty", 
"setVersion", "toString", "validate", "wait"]

In there, you can see the methods that were decorated on the Author object by grails including encodeAs and decode as well as getters and setters for explicit and implicit variables. One limitation of this method is that some methods are only added by grails dynamically once the method is executed. Notice how there isn’t an addToBooks method in the list? That’s because the method really isn’t there right now. It gets added by grails as part of a methodMissing call. We can force grails to decorate our class by calling one of the dynamic methods (like count()):

def a = new Author()
def origMethods = a.metaClass.methods*.name.sort().unique()
// call one of the dynamic methods so grails fully decorates the metaClass
def newMethods = a.metaClass.methods*.name.sort().unique()
println newMethods - origMethods  // what methods got added?

Subtracting the original methods from newly decorated method list will give you this set of dynamic methods that grails adds when you ask for a dynamic method.

["addToBooks", "count", "createCriteria", "delete", "discard", "executeQuery", 
"executeUpdate", "exists", "find", "findAllWhere", "findWhere", "get", "getAll", 
"list", "lock", "merge", "refresh", "removeFromBooks", "save", "withCriteria",

If you see a method that you want to use, but can’t remember the signature, again, all you have to do is ask:

println new Author().metaClass.methods.findAll {it.name == "withCriteria"}*.parameterTypes 

Result showing the 2 method signatures for withCriteria:

[[interface java.util.Map, class groovy.lang.Closure], [class groovy.lang.Closure]]

There is also a properties method on the metaClass that you can query in a similar manner:

println new Author().metaClass.properties*.name.sort().unique()


["books", "class", "constraints", "errors", "hasMany", "id", "log", "metaClass", 
"name", "properties", "version"]

Knowing about the information that the metaClass puts at your fingertips makes it much easier to poke around in the grails console, or with a simple script. You can learn a lot of hidden features about a class if you just know how to ask.