Is it possible to listen for superclass property changes in Groovy?

Viewed 65

I'd like to create an "reload" setter for a property, that I'd like to keep in a trait. Then any subclasses that implement the trait would use their own getter, but the trait's setter. I don't want to add any extra annotations to existing properties. Here's a clarification:

trait Tr {
    abstract String getS()
    void setS(String value) {
        ((GroovyObject) this).setProperty('s', value)
        reloadOnSChange()
    }

    void reloadOnSChange() {
        // do something when S is changed
    }
}

class Cl implements Tr {
    String s
}

Cl cl = new Cl()
cl.s = 'hello world' // reloadOnSChange should be called

Is such a property listener possible?

1 Answers

If you want to use trait for that then you have to do it differently than implementing void getS(String value) method, because Groovy compiler generates Cl.getS(String value) method that shadows the method implemented in the Tr trait.

Alternatively your trait can provide implementation of void setProperty(String name, Object value) method that if called for property s, it executes reloadOnSChange() method. Consider following example:

trait Tr {
    abstract String getS()
    abstract void setS(String s)

    void reloadOnSChange() {
        // do something when S is changed
        println "RELOAD"
    }

    void setProperty(String name, Object value) {
        metaClass.setProperty(this, name, value)
        if (name == 's') {
            reloadOnSChange()
        }
    }
}

class Cl implements Tr {
    String s
}

Cl cl = new Cl()
cl.s = 'hello world'

println "Dump: ${cl.dump()}"

Output:

RELOAD
Dump: <Cl@6580cfdd s=hello world>

The main downside is that this setProperty method will be executed for every property, so it may generate some overhead. At some point JIT should optimize the code and make sure that this if statement is executed only when property name is equal to s.

Related