Ad-Hockery

ad-hockery: /ad·hok'@r·ee/, n.
Gratuitous assumptions... which lead to the appearance of semi-intelligent behavior but are in fact entirely arbitrary. Jargon File

Groovy gotchas: overloading the assignment operator

Note

I reported this issue as GROOVY-6084 and it has been fixed as of Groovy 2.4.0-beta-4. I’m keeping the blog post here for historical interest.

When assigning to a property of an object in Groovy using the assignment operator = Groovy will look for a bean property style setter method. For example x.foo = y is equivalent to x.setFoo(y). Or is it?

Since meta-methods can be overloaded with different parameter types what will be printed to sysout if we run this script? (Remember that Groovy - unlike Java - dispatches to the most specific applicable method signature).

Object.metaClass.with {
    setFoo = {
        println "setFoo(Object)"
    }
    setFoo = { String s ->
        println "setFoo(String)"
    }
}

def o = new Object()
o.setFoo("foo")
o.setFoo(o)
o.foo = "foo"
o.foo = o

Copy & paste this into the Groovy REPL or try it on the Groovy Web Console. I think you may be surprised.

It seems like the operator form will always dispatch to the last implementation attached to the meta class regardless of parameter types whereas the method form will dispatch "correctly".

When I first encountered this problem I assumed it was a peculiarity of meta class usage but actually it’s not. Consider this example:

class Fnord {
    void setFoo(Fnord o) {
        println "setFoo(Fnord)"
    }
    void setFoo(String s) {
        println "setFoo(String)"
    }
}

def o = new Fnord()
o.setFoo("foo")
o.setFoo(o)
o.foo = "foo"
o.foo = o

The result is the same: the operator form dispatches to the last declared version of the method rather than the one that matches the type of the right hand side of the expression.

In both these examples I’ve used a String parameter on the last variant of the method. Anything can be coerced to String automatically by Groovy so although the result is not what I expected no exception is thrown. What if the method declarations in the last example are reversed, though?

Oh.

org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot ocast object 'foo' with class 'java.lang.String' to class 'Fnord'

Looking at the stacktrace there it looks like this problem might be down to the fact that the assignment operator is not, in fact, a simple alias to the setter but uses GroovyObject.setProperty.

Web Statistics