Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between metaClass.methods and metaClass.metaMethods?

If I add a meta method to a class, I would expect it to show up in Class.metaClass.metaMethods. But this seems not to be the case. In particular, if I do this:

class Example {
    def realFoo() { "foo" }

}
Example.metaClass.metaFoo = { -> "foo" }

def reals = Example.metaClass.methods*.name.grep{it.contains("Foo")}
def metas = Example.metaClass.metaMethods*.name.grep{it.contains("Foo")}

println "reals = $reals, metas = $metas"

I would expect output of reals = [realFoo], metas = [metaFoo], but I actually get reals = [realFoo, metaFoo], metas = [].

It looks like new meta methods are stored in methods, not metaMethods. So, what is the difference between metaClass.methods and metaClass.metaMethods?

like image 958
ataylor Avatar asked Feb 03 '11 18:02

ataylor


1 Answers

MetaMethods contains those methods that are decorated on the class by Groovy, but aren't actually a direct part of the class or it's inheritance structure, or that have been manually inserted into the class through the metaClass.

These are defined in the DefaultGroovyMethods class.

Depending on the type of object you're instantiating, it's mostly iterators like each, collect, find, etc.

This modification to your code shows the methods that are meta only, "real" only, and shared:

class Example {
    def realFoo() { "foo" }

}
Example.metaClass.metaFoo = { -> "foo" }

def reals = Example.metaClass.methods.name.sort().unique()
def metas = Example.metaClass.metaMethods.name.sort().unique()

def metaOnly = metas - reals
def realOnly = reals - metas
def shared = reals.findAll { metas.contains(it) }

println """
metaOnly = $metaOnly
realOnly = $realOnly
shared = $shared
"""

Result:

metaOnly = [addShutdownHook, any, asBoolean, asType, collect, dump, each, eachWithIndex, every, find, findAll, findIndexOf, findIndexValues, findLastIndexOf, findResult, getAt, getMetaPropertyValues, getProperties, grep, hasProperty, identity, inject, inspect, is, isCase, iterator, metaClass, print, printf, println, putAt, respondsTo, sleep, split, sprintf, use, with]
realOnly = [equals, getClass, getProperty, hashCode, metaFoo, notify, notifyAll, realFoo, setProperty, wait]
shared = [getMetaClass, invokeMethod, setMetaClass, toString]

All of the metaOnly and shared methods are in DefaultGroovyMethods. All of the "real" methods are on the class itself, or on it's parent class (Object in this case), plus a couple of groovy things directly related to the metaClass to get/set the metaClass as well as getProperty/setProperty and invokeMethod which allow you to override method behavior.

If you want to search through all of the methods to see what exists, I use something like this:

def allMethods = (Example.metaClass.methods + Example.metaClass.metaMethods).name.sort().unique() 
like image 99
Ted Naleid Avatar answered Nov 04 '22 03:11

Ted Naleid