76. Getters and Setters

Getters and setters are methods:

  • Getters are used to read the value of a member variable
    • Their name begins with get followed by the title-case version of the property name
    • getName, getEmail, getMobile
    • Getter methods don’t have any parameters
  • Setters are used to set the value of a member variable.
    • Their name begins with set followed by the title cased version of the property name
    • setName, setEmail, setMobile

In Java code you often see a lot of anaemic setters and getters - basically the getters just return name and the setters just this.name = name. In a class with a lot of member variables this results in a lot of boilerplate code that really gets in the way of readability. Groovy, however, creates these anaemic setters/getters for properties behind the scenes so you don’t have to. In the next code example I use my basic Person class with three properties:

Where did getEmail come from?
class Person {
    def name
    def email
    def mobile
}

def jess = new Person()
jess.setEmail('jess_at_example.com')
println jess.getEmail()

I never wrote the getEmail and setEmail methods - Groovy just worked out that my email property would need associated getters and setters.

This means that most Groovy classes only need to provide getters/setters for properties if they need specific functionality or want to turn off the default behaviour. In the code below I define the setter for the email field as I want to make sure the caller is passing me a valid email address:

A custom setter
class Person {
    def name
    def email
    def mobile

    void setEmail(email) throws IllegalArgumentException {
        def regex = ~/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+/
        if (email.matches(regex)) {
            this.email = email
            return
        }
        throw new IllegalArgumentException('Incorrect email format')
    }
}

def jess = new Person()

jess.setEmail('jess@example.com')
println jess.dump()

//This will fail
jess.setEmail('jess_at_example.com')
println jess.dump()

What if I don’t want a setter or getter? You can define your own setter or getter, mark each with the private access modifier and document it. I also like to throw exceptions to really prove my point:

Change my ID will you?
class Person {
    def id
    def name
    def email
    def mobile

    /**
     * Will NOT set the ID - do not call
     * @throws Exception Always throws an exception
     */
    private void setId(id) throws Exception {
        throw new Exception('Method not available')
    }
}

In the code above you’ll see that any call to setId will cause an exception to be thrown - whether it calls the setter directly (p.setId 1234) or indirectly (p.id = 1234).

Fields

Setters and getters aren’t generated for fields. You need to create your own setters and getters for fields (if you want them). In the code below we can’t use p.setName('Bill') as the setter is not created for us - instead, we access the field directly with p.name = 'Bill':

A basic field
class Person {
    private String name
}

Person p = new Person()
p.name = 'Bill'
println p.dump()

If you do provide a setter for a field (such as setName in the code below), an attempt to directly set the field’s value (e.g. p.name = 'Bill') is deferred to the setter:

A setter
class Person {
    private String name

    void setName(name) {
        println 'Setting name...'
        this.name = name
    }
}

Person p = new Person()
p.name = 'Bill'
println p.dump()

p.setName 'William'
println p.dump()

The call to p.setName also works but using p.name is a little cleaner in terms of aesthetics/style.

Pseudo properties

The Groovy model for setters and getters means that you can access Java and Groovy getter/setter methods as if they were properties. For example, instead of calling myUrl.getText() on an instance of java.net.URL, I can just use myUrl.text:

def myUrl = new URL('http://www.example.com')
print myUrl.text

The URL class may have an underlying member variable called text but that’s not really our concern. What it does guide us to is the fact that, provided we use the get and set prefix on methods we can then access them as if they were properties. Consider a Person class that stores the person’s date of birth (DOB). When we know their DOB we can calculate their age:

Age is a pseudo property
import java.time.LocalDate
import java.time.Period

class Person {
    String name
    LocalDate dateOfBirth

    Integer getAge() {
        Period.between(dateOfBirth, LocalDate.now()).years
    }
}

Person jim = new Person(dateOfBirth: LocalDate.of(1983, 06, 04))
println jim.age

In the code above I define the getAge getter to perform the calculation to determine the person’s age. This is better than having an age property/field as this can be calculated from the DOB. The other advantage is in long-running systems - if we stored the age then it becomes out of date as the system runs for days/weeks/months.

It’s important to note that we can’t call jim.age = 100 in the code above as we don’t provide a setter. Naturally, we could provide a setter but it doesn’t make a lot of sense in terms of the Person’s age.