Groovy method overriding

One of the pieces of the project I am currently working on is using Grails and GORM for rapid deployment. In one of the GORM objects I wanted to override one of the methods that Groovy creates for a property so I can derive some information from the property at the time it was set.

It sounds simple enough but, like most things once you dive into it, it wasn’t. Even after a good bit of Googling I could only find partial answers or comments in posts that led you part way to the solution.

So, let me present to you the situation in more detail and the solution so you don’t have to do all the digging and Googling that I had to.

Lets begin with a sample GORM object

class Person {
    String firstName
    String lastName
}

Simple enough. Now, what if we wanted to add a property like isLastNameSmith. How would you do it?

Well one way would be:

class Person {
    String firstName
    String lastName
    boolean lastNameSmith = false
}

That would work but you are now forcing the developer to check the last name and relying on them to set the boolean. In a large scale project with many developers, this would not be a chance I would want to take. It would be too easy to forget to set the boolean.

Ok, so you decide “Well, I’ll set it for them when the lastName is set”. Good idea! Not only to you take the burden off of the developer, if the value changes in the DB the flag will be correct as soon as the object is loaded.

So, what’s involved in this little slick solution? Well, first, we have to make lastNameSmith private so that the developer can’t change it. Well, if we make it private we have to provide a method to check it. Lets stop here and write that code:

class Person {
    String firstName
    String lastName
    private boolean lastNameSmith = false

    def isLastNameSmith() {
        return lastNameSmith
    }
}

Great! You now have locked down the access. All you need to do is check if the last name is ‘Smith’ when the last name is set. So, you think to yourself “Easy enough, I’ll just do:”

    def setLastName(s) {
        lastName = s
        if (lastName == 'Smith') lastNameSmith = true  // Lets be case sensitve to keep the example easy
    }

And…. you’d be half right and just lucky at the same time. Wait, what? Sorry, but it wouldn’t catch all the ways to set last name. Well, why not?

First, lets remember that you can set a property in Groovy in two ways:

  • method call – object.setLastName(“x”)
  • direct setting – object.lastName = “x”

In Groovy you need to handle both cases. Lets address the first, the method call.

If you code:

    def setLastName(s) {}

Groovy generates:

    public Object setLastName(Object s) {}

Now, you got lucky. Your code, when calling object.setLastName("x") will match the generated Groovy method. But it might not always do that. Say you want to override a boolean setter like so:

    def setIsSomething(boolValue) 

Groovy generates:

    pubic Object setIsSomething(Object boolValue)

This would not be what you wanted because you are actually looking for:

    public void setIsSomething(boolean boolValue)

So, what you need to do is explicitly override the exact java syntax method call to ensure that your code gets called. If you don’t do that you risk the possibility in certain instances of not having the methods match and your override method not being called.

Next is the other way Groovy allows you to easily set properties, via direct reference like object.lastNameSmith. Now yes, you could do this in standard Java as well though it is advised against for maintenance reasons unless you are really concerned about performance (it is one of the performance optimizations suggested for Android development by Google).

In this case you may thing to yourself, well, now I’m screwed. And you’d be wrong again! (Hey, making mistakes is sometimes the best way to learn ;)). Groovy doesn’t directly set the property when invoked like object.lastNameSmith, it calls a method it creates named setProperty(String name, Object value). Since we know Groovy is going to do this, we can override that method and catch the property call to lastName!

So, the second part of the solution would look like:

   public void setProperty(String name, Object value) {
       def metaProperty = this.metaClass.getMetaProperty(name)
       if(metaProperty) metaProperty.setProperty(this, value)
       if (name == "lastName" && value? == "Smith") this.isLastNameSmith = true;
   }

The meta stuff is just what Groovy does to normally set a property. We still need to do this to support all the other properties that will be set as well as handle the case we are looking for.

So, our final object would be:

public Person {
    String firstName
    String lastName
    private boolean lastNameSmith = false

    def isLastNameSmith {
        return lastNameSmith
    }

    public void setLastName(String s) {
        this.lastName = s;
        if (s == "Smith") lastNameSmith = true;
    }

    pubic void setProperty(String name, Object value) {
        def metaProperty = this.metaClass.getMetaProperty(name)
        if(metaProperty) metaProperty.setProperty(this, value)
        if (name == "lastName" && value? == "Smith") this.lastNameSmith = true;
   }

This solution covers both ways of a property being set and ensures that the developer doesn’t have to worry about checking the last name to set a boolean as well as making sure that any changes in the database will be reflected in the object.

About sseaman

Connect with me on Google+
This entry was posted in Groovy and tagged , . Bookmark the permalink.

Leave a Reply

Your email address will not be published.