Question

I am trying to access a method of an instance using the metaclass, but I get the error that the property does not exist. Is there a way to access properties of classes declared in another class.

Here is a contrived example:

class DogFood {
   def ft = 'food!'
   def foodType() { ft}
}

class Dog {
  def bark() { println "woof!" }

  DogFood df = new DogFood()
  def ft() { println df.foodType()} 

  def getDf() {
    df
  }
}

def doAction( animal, action ) {
  animal."$action"()
}


def rex = new Dog()

println rex.df.ft  //works

def barkString = "bark"

doAction( rex, barkString ) //works
doAction( rex, "df.ft") //doesn't work
doAction( rex, "getDf().ft") //does not work

Is there a way to access df.ft or getDf().getFt() using Groovy's metaclass methodology?

Thanks in advance

Was it helpful?

Solution

I don't think you can access anything with a . in the string name using that notation. You can do it with Eval.x though.

def doAction( animal, action ) {
    Eval.x(animal, 'x.' + action)
}

Edit: Also be aware that there is risk involved in this method if you are executing user input. You can use Eval to execute arbitrary code. For example,

groovy:000> foo = [bark: { println 'bark' }]
===> {bark=groovysh_evaluate$_run_closure1@db2e44d}
groovy:000> Eval.x(foo, 'x.bark()')
bark
===> null
groovy:000> Eval.x(foo, 'x.bark(); println "executing more code"')
bark
executing more code
groovy:000>

OTHER TIPS

doAction(rex.getDF(), "ft") should work, although that's not quite what you are after.

You can also build up anything you want in quotes, so I think something like:

"${doAction( rex, df.ft )}" would work.

I believe the specific problem you are running up against is that the "$action" syntact referrs explicity to a property of the class you are calling against, so your combined stuff doesn't seem to work.

I believe that if dfs="df" and fts="ft" then rex."$dfs"."$fts" might work but you'd have to tweak your doAction method..

--- By the way.

If you're just messing around trying to code something for yourself or a very small team, the type of solution you are going for is fine, but this isn't really a "good" way to solve this problem, overall I'd try to find a better pattern. It seems neat at first but:

  • A) it's not obvious
  • B) it'll make your tools less functional (ask eclipse to find all uses of the "ft" method--it'll miss this)
  • C) it moves checks to runtime that could be done at compile time
  • D) less maintainable for all the above reasons and a few more.

    If I were doing this for code I had to deal with long-term I would try to solve it in pure Java first, then optomize that solution over to groovy.

    The most ovious way is that you have an interface with eat() and bark() that all your animals implement--your code could usually choose which one to call directly.

    If you can't determine what you wish to call directly from code (like if you expect the user to type "bark"), I'd use the command pattern--a "command" object for eat and a different oen for bark that can each be attached to any animal via an interface.

  • Licensed under: CC-BY-SA with attribution
    Not affiliated with StackOverflow
    scroll top