VIII Object-oriented programming
72. Introduction
This section introduces a range of object-oriented terminology and provides you with a set of resources that will help you really hone those object-oriented programming skills.
Object-oriented programming is often referred to by its acronym “OO” or “OOP” and has been around for some time. We won’t delve into history here - check out Wikipedia if you’d like to learn more. For now, let’s describe the essentials of OOP through a Groovy lens.
The object-oriented programming approach models concepts based on a combination of properties and methods. The concept being modelled may be a real-world entity such as a person, a car or a tree. It may also be a “virtual” concept such as a bank account, an HTTP server or a sales report.
The properties of an object (also referred to as fields) hold data that is important to the object’s state.
Methods (less often referred to as functions) provide a means of accessing and manipulating the object’s properties and behaviours. Encapsulation is an important concept in OOP and relates to the use of methods to hide and even protect an object’s properties from direct access. Essentially, other code interacts with the object via its methods - often referred to as its interface.
73. Expando
Before getting stuck into formal object definitions, let’s take a look at the Expando class that ships with Groovy. Expando provides a very flexible way to construct an object on the fly.
In the example below the monster variable is dynamically assigned a set of fields and methods (through the use of closures).
def monster = new Expando()
monster.with {
//Let's add some properties
name = 'Dr Frankensteins Monster'
owner = 'Dr Frankenstein'
height = 8
//Add some closures (methods)
getOwner = { owner }
getName = { name }
getHeight = { height }
scareVillage = { village ->
println "Look out $village here comes $name!"
}
}
monster.scareVillage('Darmstadt')
The monster Expando looks to be acting in a manner similar to a Map but it gives us more functionality. Whilst the properties can be assigned to an Expando instance in the same way as a Map, the addition of a closure illustrates the difference. In the example below the use of a Map to define the vampire variable demonstrates that the scareVillage can’t rely on an instance field (name) so must refer back to the vampire map:
def vampire = [ : ]
vampire.name = 'Dracula'
vampire.scareVillage = { village ->
println "Look out $village here comes $vampire.name!"
}
vampire.scareVillage('London')
By using the Expando class instead of a Map, the instance fields can be accessed.
Using Expando with CSV data
Expando can be useful when we want to consume data from a source (such as a file) and manipulate it as an object. One such example is an application that reads a comma-separated file (a CSV) that contains a header row and multiple subsequent data rows. The header row tells us the fields in our data objects and we can start to throw in some methods to help handle our data.
I’ll start with a full listing of a script that accepts CSV data (I’ll just use a String but it could be from a file) about various contacts. Expando will help me then produce a vCard for each contact. Take a read of the full listing and we’ll then break it down into easier chunks.
//This is a basic CSV (Comma-separated file) described as a string
def table = '''\
name,email,phone
Fred,fred@email.com,555-678345
Alex,alex@email.com,555-987123
Simon,simon@example.com,555-567321
'''
//We define the separator as the comma character
def separator = ','
//Read the CSV in and break it up by linebreaks
def csv = table.tokenize()
//Extract the first row - it contains the headers (field names)
header = csv[0].tokenize(separator)
csv.remove(0)
def contactList = [ ]
for (row in csv) {
Expando contact = new Expando()
//Now setup the contact class with field names based on the header
row.tokenize(separator).eachWithIndex { key, value ->
contact.setProperty header[value], key
}
contact.getVCard = {
"""BEGIN:VCARD
VERSION:4.0
N:$name
EMAIL:$email
TEL;TYPE=work,voice;VALUE=uri:tel:$phone
END:VCARD"""
}
contactList << contact
}
contactList.each {
println it.vcard
}
The handy tokenize method is used to break the CSV data into individual rows. The first row is actually a header row and we perform another tokenize to extract each header item (field) and then remove the header row from the data table:
def csv = table.tokenize()
header = csv[0].tokenize(',')
csv.remove(0)
A contactList is then defined - this will hold our list of contacts:
def contactList = []
The code now iterates through the data table. For each row, an instance of Expando is declared:
Expando contact = new Expando()
tokenize is again used to separate each field in the row. The eachWithIndex method passes the field’s value and its index into the closure’s key and value parameters respectively. I use the index to work out the field name in header and set the value as the field from the row:
//Now setup the contact class with field names based on the header
row.tokenize(',').eachWithIndex { key, value ->
contact.setProperty header[value], key
}
The getVCard closure is then added to the contact - this will construct a vCard for use in other systems:
contact.getVCard = {
"""BEGIN:VCARD
VERSION:4.0
N:$name
EMAIL:$email
TEL;TYPE=work,voice;VALUE=uri:tel:$phone
END:VCARD"""
}
contactList << contact
Once each row has been consumed we can display our contactList:
for (contact in contactList) {
println contact.getVCard()
}
74. The basics of OO
This chapter will provide a whirlwind tour of the various object oriented programming concepts supported by Groovy.
Classes
Object-oriented programmers use classes to classify objects. As such, a class defines the properties (fields)1 and methods of an object.
In Groovy the class keyword if used when declaring a new class:
class Person {}
Objects consist of properties (data) and methods for interacting with that data. For example, the code below describes a Person class with a name property and a getName method:
class Person {
def name
def getName() {
return this.name
}
}
In order to create an instance of the Person class we use the new keyword and assign the result to a variable
def john = new Person(name: 'John')
println john.getName()
The call to new Person(...) is actually using a special method called a “constructor”. Classes can define a variety of constructors to help with creating an instance and Groovy has some handy tricks we’ll explore later.
Instead of using the def keyword the variable could be declared as being of the Person type:
Person john = new Person(name: 'John')
Instance and Class Elements
In the getName method you’ll notice the keyword this is used. You can translate this to mean “this instance”. Groovy supports instance properties and methods - those operating on a specific instance of the class. This means that your code isolates one instance from another and prevents you from inadvertently altering instances.
Static fields/properties and methods are also supported - these apply at a class level. By preceding a field or a method with the keyword static we indicate that it is relevant to all instances of the class. In the example below I set the specie field to be static and this means I access it at the class level by referring to Person.specie.
static keywordclass Person {
static specie = 'homo sapien'
def name
}
println Person.specie
Constants
An (English) language lawyer might think that the keyword static indicates that the value of specie can’t be changed. However, if you try Person.specie = 'tubulidentata' you’ll see that we can turn Person into a different specie! This is definitely not what we want so we need to declare specie as a constant.
The final keyword precedes static/instance fields and methods to make them constant:
finalclass Person {
static final SPECIE = 'homo sapien'
def name
}
Constructors
Constructors are a special type of method that is called when a new instance is created. We saw that Groovy provides a built-in constructor when we called new Person(name: 'John'). This takes a map in which the keys match the fields in the Person object.
To define a custom constructor we define a method with the same name as the class but without the def preceding it. The example below declares a constructor this way (Person(name)):
class Person {
def name
Person(name) {
this.name = name
}
def getName() {
this.name
}
}
def john = new Person('John')
println john.dump()
Overloading
The overloading feature of Groovy classes allows us to create multiple versions of the same method. The parameter list for each version of the method varies to allow callers to provide either a different number of parameters (as in the example below) or with the same number of parameters but with different types. Overloading is useful but also consider using default values for parameters as this can help reduce the number of methods you need to write and maintain.
mean methodclass Math {
static mean(num1, num2) {
(num1 + num2) / 2
}
static mean(... nums) {
nums.sum() / nums.size()
}
}
println Math.mean(10, 20)
println Math.mean(2, 4, 6, 8)
Interfaces
Interfaces provide a method for defining programming interfaces. Interfaces, for the most part, just define method signatures and not their implementation. Classes implement these interfaces in a manner that reflect the class’s role/model/context.
The example below defines an interface named Exercise with a single method run. The Athlete class then implements the interface:
interface Exercise {
def run(int distance)
}
class Athlete implements Exercise {
def name
def run(int distance) {
println "I'm off for a ${distance}km run."
}
}
def robert = new Athlete(name: 'Rob')
robert.run(10)
Inheritance
A superclass is one from which other classes inherit functionality. The “child” classes are referred to as being subclasses. A subclass inherits from a superclass through the use of the extends keyword.
In the code below, StaffMember is a subclass of Person. This allows StaffMember to access the name field defined in Person:
class Person {
def name
}
class StaffMember extends Person {
def staffID
def getIdentification() {
println "${this.name} - ${this.staffID}"
}
}
def sally = new StaffMember(name: 'Sally', staffID: 765)
sally.identification
Unlike interfaces, superclasses can provide implemented methods and fields that subclasses can utilise. However, Superclasses can work somewhat like interfaces and their methods can be declared as abstract to force subclasses to provide their own implementation.
Overriding methods
Subclasses can also override methods and fields implemented by superclasses. This lets subclasses provide more contextual implementations if needed. A subclass can refer directly to superclass fields and methods by using the super keyword.
In the example below, StaffMember is a subclass of Person. The StaffMember class overrides the getName method and prefixes a string to the name returned by the superclass.
class Person {
def name
def getName() {
this.name
}
}
class StaffMember extends Person {
def staffID
@Override
def getName() {
"Team member ${super.name}"
}
def getIdentification() {
println "${this.name} - ${this.staffID}"
}
}
def sally = new StaffMember(name: 'Sally', staffID: 765)
println sally.name
Traits
At first glance, traits can be considered as interfaces with an implementation but they offer a really useful approach to adding features or abilities to a class. A common example may be to add the flying trait to animals or vehicles. The trait may have its own fields and/or methods.
In the example below:
- A very basic
Projectclass is defined. It just stores the project name - An
Agiletrait provides some basic fields used to describe an agile aspect to projects. A very basic method (startIteration) gives us an example method to call. - A
Scrumclass is defined:- It extends the
Projectclass - It implements the
Agiletrait - Two Scrum-specific fields are added (
productOwner&scrumMaster)
- It extends the
- I can then create an instance of
Scrumand provide it information for both theProject/Scrumhierarchy as well as theAgiletrait.- I then call the
Agiletrait’sstartIterationmethod and our project is away!
- I then call the
class Project {
def name
}
trait Agile {
def iterationLength = 4
def backlog = [ ]
def developmentTeam = [ ]
def startIteration() {
println """\
We're staring our $iterationLength week iteration for $name
Team members: $developmentTeam
Backlog: $backlog
"""
}
}
class Scrum
extends Project
implements Agile {
def productOwner
def scrumMaster
}
def project = new Scrum().with {
name = 'Project X'
iterationLength = 2
productOwner = 'Charlie'
scrumMaster = 'Bobby'
developmentTeam = [ 'Dean', 'Sam' ]
backlog << 'As a User I want to press buttons'
backlog << 'As an Admin I want to lockout users'
//This returns 'it' (the new instance of Scrum)
it
}
project.startIteration()
Packages
As discussed earlier, Groovy allows programmers to group objects into packages. In the following snippet the Person class is allocated to the myobjects package:
package myobjects
class Person {}
Packages are central to using others’ code in your programs and allowing your code to be used elsewhere.
Summary
This chapter has provided an extremely brief overview of object-oriented programming supported by small Groovy examples. The following chapters in this section will explore fundamental aspects of fields and methods and the subsequent section will dive far deeper into how to really get OO with Groovy.
- At this stage I’ll use ‘field’ and ‘property’ interchageably - there is a difference but I’ll discuss this soon.↩
75. Properties and Fields
In this chapter we’ll delve more into properties and fields. They’re almost the same thing and are known more generally as member variables as they hold data regarding the state of an object instance. In less technical terms, they help describe a specific instance (e.g. Jim) of a class (Person).
Properties
The class below is composed of three properties and could be used to pass a person’s details around in a system. You may come across references to this type of structure as “Groovy beans”:
class Person {
def name
def email
def mobile
}
def astrid = new Person()
astrid.name = 'Astrid Smithson'
astrid.email = 'astrid@example.com'
astrid.mobile = '0418 111 222'
println astrid.dump()
In the code above I create a new instance of the Person class by calling new Person() and can then access each property using dot-point notation. This type of class can be really handy when you just need a structure for storing and handing around data - say, for example, you load a record from a database and want to feed it to other classes and their methods for processing.
Groovy provides a built-in approach to passing in property values when creating a new instance. The code below creates a new Person but sets the property values using named arguments:
def astrid = new Person(name: 'Astrid Smithson', email: 'astrid@example.com', mo\
bile: '0418 111 222')
We don’t have to set all of the properties, just the ones we need:
def astrid = new Person(name: 'Astrid Smithson', email: 'astrid@example.com')
Viewing an object’s properties/fields
If you try to use println to display the details for astrid you’ll get something like Person@46423706. It’s not very useful - just the type of the Object and an identifier. If you want to find out the field values, call the dump method that all objects inherit from Object:
println astrid.dump()
Typed properties
Just as we saw with variables, properties can be defined with a specific data type, rather than with a dynamic type. The code below redefines the Person class with typed properties:
class Person {
String name
String email
String mobile
}
People often ask if they should just use def or assign an explicit type. My answer is pretty straight-forward: if you know how you’ll use it and you’ll only use it one way, give it a type. The name property is a good example and I would use String. In other situations you may want to be flexible in what you can store so use def.
Fields
A field is pretty much the same thing as a property - it’s a member variable - but there’s a difference: fields are defined with an access modifier and properties are not. An access modifier is used to allow/block access from other code using the class and its member variables and methods.
In the code below I have rewritten the Person class using fields rather than properties:
class Person {
public name
private email
private mobile
}
Firstly, you’ll see that I haven’t used the def keyword - I don’t need to do this when I provide an access modifier. I could have declared public def name but it’s not good style as the def is implied. We can specify a type for a field by stating the type after the access modifier:
class Person {
public String name
private String email
private String mobile
}
Default values
Properties and fields are much like variables and can be declared with an initial value:
class Person {
public String name = 'anonymous'
private String email = 'anon@example.com'
private String mobile = ''
}
The initial value doesn’t have to be a literal, it can be determined from an expression.
Casting maps to objects
Groovy gives bean-style objects a really interesting approach to casting1 a map or a list to an object. Let’s get an example going first:
class Person {
def name
def email
def mobile
}
def astrid = [name: 'Astrid Smithson', email: 'astrid@example.com'] as Person
println astrid.dump()
Person gretchen = [name: 'Gretchen Gaul', email: 'gretchen@example.com']
println gretchen.dump()
For the first person (astrid) I explicitly cast the map to a Person instance using
[name: 'Astrid Smithson', email: 'astrid@example.com'] as Person. Groovy creates a new instance of Person and calls
the name and email setters.
In the case of gretchen I don’t need to call as Person as Groovy infers this from the fact I declared the variable’s
type (Person gretchen).
- Remember the
asoperator?↩
76. Getters and Setters
Getters and setters are methods:
- Getters are used to read the value of a member variable
- Their name begins with
getfollowed by the title-case version of the property name -
getName,getEmail,getMobile - Getter methods don’t have any parameters
- Their name begins with
- Setters are used to set the value of a member variable.
- Their name begins with
setfollowed by the title cased version of the property name -
setName,setEmail,setMobile
- Their name begins with
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:
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:
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:
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':
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:
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:
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.
77. Methods
Methods were covered in their own section and we just looked at getters/setters so you’d think that we’ve covered it all. Well, there’s a bit more to say about methods in Groovy.
In The basics of OO I touched on instance and class elements:
- Instance elements are properties/fields and methods that relate to a specific instance of a class
- These are also referred to as member variables and member methods/functions
- Class elements are properties/fields and methods that relate to the class itself - not a specific instance
- These are covered in the chapter on Class Methods
In the code below I have two instance properties (name and email) and an instance method (getContact())
getContact is an instance methodclass Person {
String name
String email
String getContact() {
"$name <$email>"
}
}
Person jenny = new Person(name: 'Jenny', email: 'jen@example.com')
println jenny.getContact()
Person dave = new Person(name: 'Dave', email: 'dave@example.com')
println dave.getContact()
When we look at the two instances of People (jenny and dave) we can see that the call to getContact() is specific to each
instance of the class. This means that our request for jenny’s contact information doesn’t return us dave’s details.
There are times when we need to make sure we’re specific when referring to instance elements within a class. In order to do this, we use the this keyword:
thisclass Person {
String name
String email
void setName(name) {
this.name = name
}
String getContact() {
"$name <$email>"
}
}
Person jenny = new Person()
jenny.setName('Jenny')
jenny.setEmail('jen@example.com')
println jenny.getContact()
In the example above I have provided a setName(name) method and set the instance’s name property using this.name = name. This example
is demonstrating a primary use of this: to delineate between the method parameter and the instance property. this can be used for both
member variables (e.g. this.email) and when calling member methods (this.setEmail()) but is limited to elements within the class - it can’t
be called from outside the class (e.g. jenny.this.setName('Jenny')).
Overloading
I’ve deferred a really useful method feature for this section: method overloading. Overloading allows a class to declare a method with the same name more than once, provided each version of the method has different parameters.
In the example below I provide two implementations of the exercise method: one that takes no parameters and another
that takes one parameter (duration).
class Person {
String name
def exercise() {
println 'I am exercising but I\'m not sure for how long'
}
def exercise(duration) {
println "I am exercising for $duration minutes"
}
}
Person sam = new Person(name: 'Sam')
sam.exercise()
sam.exercise(10)
Whilst my example has just two versions of the exercise method, I could keep adding more
(def exercise(duration, location), def exercise(duration, location, activity)) to cover all of the cases I need. In many cases
I’ll need a baseline of functionality and, in order to save me writing this functionality into each version of exercise, I can
call from one method to another:
class Person {
String name
def exercise() {
//Generate a random number using java.util.Random
Random random = new Random()
//Use nextInt to get a random number between 0 and 120
exercise(random.nextInt(120))
}
def exercise(duration) {
println "I am exercising for $duration minutes"
}
}
Person sam = new Person(name: 'Sam')
sam.exercise()
In the previous versions of exercise I haven’t used parameter types so my overloads must be differentiated by the number of parameters
for each version of the method. However, if I provide specific parameter types I can have several variations of a method,
differentiated by the parameter types:
class Person {
String name
def exercise() {
println 'I am exercising but I\'m not sure for how long'
}
def exercise(Integer duration) {
println "I am exercising for $duration minutes"
}
def exercise(String activity) {
println "I think I'll go for a $activity"
}
def exercise(String activity, Integer duration) {
exercise(activity)
exercise(duration)
}
}
Person sam = new Person(name: 'Sam')
sam.exercise()
sam.exercise(10)
sam.exercise('walk')
sam.exercise('jog', 30)
Whilst I could have written another exercise method with exercise(Integer duration, String activity) I have already provided
exercise(String activity, Integer duration) so such an addition would, at best, be redundant but could also be confusing if
each variation did something functionally different. For example, say exercise(Integer duration, String activity) were to be written as:
def exercise(Integer duration, String activity) {
println "I'll go for a $activity in $duration minutes"
}
I can technically do this as the parameters are different to exercise(String activity, Integer duration) but you can see
that my intent is different to the intent seen in the other exercise methods. Avoid this type of coding - it really is a trap. Overloading
best works when you have the same functionality that can work with different parameters. Using overloading to provide different functionality, depending on parameters,
is likely to trip you up. Instead of overloading with that last example, I would have given the method a different signature that
better reflects what the method is doing: delayExercise(Integer duration, String activity).
Default parameter values can make this confusing
Try the code below in a groovyConsole:
class Person {
String name
def exercise() {
println 'I am exercising but I\'m not sure for how long'
}
def exercise(duration = 10) {
println "I am exercising for $duration minutes"
}
}
Person sam = new Person(name: 'Sam')
sam.exercise()
sam.exercise(10)
Unfortunately that example won’t even run - Groovy reports that method "java.lang.Object exercise()" that is already defined.
It’s not always clear where this clash lays - after all, I have explicitly written two versions of exercise, each with a different parameter list.
However, my use of a default value for a parameter (exercise(duration = 10)) is implicitly defining two versions of the exercise method:
-
exercise()- this is what clashes with the explicitly declaredexercise() exercise(duration)
In larger classes this can get a little confusing so, when it happens to you, start looking at the overloaded methods with default parameter values.
Overloading != Overriding
Before we move on, it’s worth highlighting two pieces of terminology:
- Overloading is when a class is defined with methods with the same name but different parameters to others within the class or its superclass(es).
- Overriding is when a subclass redefines a method from its superclass. We’ll see this in the chapter on Inheritance
When we looked at Operator Overloading we saw that we could write a plus method that aligns with the
+ operator. The overloading aspect indicates that the functionality for the operator (+) is being defined for certain operands
and this is using different parameter types in overloading.
78. Constructors
A constructor is a special type of method that is called when instantiating a new instance of a class. We create a new instance every time we use the new keyword:
class Person {
def name
def email
def mobile
}
def astrid = new Person()
In the code above I define the Person class with three properties and then create a new instance in the astrid variable. The new keyword indicates to Groovy that a new Person instance is to be created. The Person() aspect is actually a call to the constructor for the class. However, I haven’t actually provided a constructor for the Person class so what am I calling? Groovy classes all trace back to the Object class - where a class does not explicitly state that it inherits from (subclasses) another class it is automatically seen as a subclass of Object.
Groovy sees Person() and knows that the Person class doesn’t provide a no-parameter (aka no-argument or no-args) constructor but Object does so that is called and it doesn’t really do anything interesting. This whole arrangement means that your classes automatically have a no-argument constructor.
As always, Groovy adds a little extra on top and we’ve seen that we can use the map notation to assign values to member variables (properties/fields) when we create a new instance:
def astrid = new Person([name: 'Astrid Smithson', email: 'astrid@example.com'])
You can see that new Person is being passed a map consisting of keys that relate to properties in the class. This means you can prepare the map elsewhere in your code and then pass it into the constructor:
def details = [name: 'Astrid Smithson', email: 'astrid@example.com']
def astrid = new Person(details)
Groovy gives us that little bit of syntactic sugar and lets us drop the square brackets ([]) in the call:
def astrid = new Person(name: 'Astrid Smithson', email: 'astrid@example.com')
This looks like a built-in constructor that accepts a map (or map-like) syntax in which each of the keys match a name of a member variable. This is really useful for bean-type classes that we’re using to keep data fields together. However, this isn’t a true constructor as Groovy doesn’t generate a Person constructor that takes a map as its parameter. Instead, the no-args constructor is called followed by the setters for each key in the map. This isn’t usually a problem until you’re using final properties and fields.
Writing your own constructor(s)
Whilst the no-args constructor and map parameter approach can be useful, you’ll probably need to define your own constructors at some point. This may be due to a few reasons, such as:
- You need information when you create a new instance that is more than just the member variables - perhaps you need to calculate something
- You don’t want the caller to be able to populate a member variable - perhaps you’ll load that from a database or perform a calculation to determine its value
If you need to do something specific in order to sensibly create a new instance of your class you’ll need to define one or more constructors. So, let’s look at an example:
Personclass Person {
def name
def email
def mobile
//Here's the constructor:
Person(name) {
this.name = name
}
}
def astrid = new Person('Astrid Smithson')
println astrid.dump()
In order to define a constructor we declare a method that:
- Can have an access modifier (discussed in a later chapter)
- Has no return value declared - none, not even
void - Has the same name as the class in which it is defined - yes, it is case-sensitive
- Can take 0 or more arguments
Now that you’ve supplied a constructor you’ll lose the built-in “map constructor”. Be warned that this isn’t always obvious! If we create a new instance using named arguments, our dump will show us that astrid’s name becomes a list:
def astrid = new Person(name: 'Astrid Smithson', email: 'astrid@example.com', mo\
bile: '0418 111 222')
println astrid.dump()
This will display: <Person@45ceff52 name=[name:Astrid Smithson, email:astrid@example.com] email=null mobile=null>. You should be able to see that the name property is just plain wrong.
By adding the Person(name) constructor we’ve effectively changed Person so that it has two constructors: the no-argument constructor and one that accepts the Person’s name. If the coder using our Person class wanted to add an email or mobile number they’d have to do that after instantiating the instance.
We can define as many constructors as we feel necessary - they all carry the same name as their class but have different parameter lists (Ã la overloading). In the code below I provide two constructor definitions:
class Person {
private name
def email
def mobile
Person(name) {
this.name = name
}
Person(name, email) {
this(name)
this.email = email
}
}
def astrid = new Person('Astrid Smithson', 'astrid@example.com')
println astrid.dump()
You can probably work out what most of the code above does - one constructor allows for just a name argument and the other allows for both a name and an email. But what is this(name) doing? First, remember that this is used to refer to the current instance. What I wanted to do from Person(name, email) was call Person(name) so as to set the name - perhaps I was going to put some more complex logic in around setting the name. In order to call the Person(name) constructor from another constructor I need to to refer to this so this(name) is a call to another constructor within the class. This is commonly referred to as “constructor chaining”.
There is a specific rule we have to follow when constructor chaining - calls to other constructors must occur before any other statements. That means the following constructor code won’t compile:
Person(name, email) {
this.email = email
this(name)
}
Before moving on, there’s still that no-args constructor that we get even when we don’t ask for it. Sometimes we actually want to force
other developers to use a constructor with arguments. For example the Person(Integer id) might use the id parameter to load the
Person’s details from a file/database. In this case the no-args constructor could leave us with some sort of zombie person instance
with no information. One approach to avoiding this is to prepend the private modifier to indicate
that other coders shouldn’t use it:
private Person() {}
If you want to be really forceful, just throw an exception if someone tries their luck:
private Person() {
throw new IllegalArgumentException('Do not use the no-arg constructor')
}
Lists
Good old Groovy never stops giving us different ways to do things and, in the code below, we can cast a list of values into
a Person:
class Person {
String name
String email
String mobile
Person(name, email) {
this.name = name
this.email = email
}
Person(name, email, mobile) {
this(name, email)
this.mobile = mobile
}
}
def astrid = ['Astrid Smithson', 'astrid@example.com', '12345'] as Person
println astrid.dump()
Person gretchen = ['Gretchen Gaul', 'gretchen@example.com']
println gretchen.dump()
For astrid I cast the three item list explicitly using as Person because I didn’t supply a type when decalaring the
variable (def astrid). Groovy takes my 3 items, sees that there is a three-argument constructor, and passes it through.
In the case of gretchen I don’t need the explicit cast as I declared a type with the variable (Person gretchen). The
list has 2 items so the two-argument constructor is called.
There’s two really interesting things to keep in mind with this:
- The constructor is being called (unlike with the “map constructor”)
- This will work with all the various Groovy and Java classes
On that last point, let’s look at two quick examples to prove I’m not fooling you:
//Prepare a calendar using YYYY, MM, DD
def calendar = [2015, 01, 31] as GregorianCalendar
//Setup a URL
java.net.URL url = ['http', 'www.example.com', 80, '/index.html']
TupleConstructor annotation
The @groovy.transform.TupleConstructor is an annotation that we can add to our classes and have a variety of constructors automatically generated for us:
TupleConstructor annotation@groovy.transform.TupleConstructor
class Person {
def name = 'Anonymous'
def email
def mobile
}
def unknown = new Person()
def astrid = new Person('Astrid Smithson')
def john = new Person('John Hancock', 'john@example.com')
def kelly = new Person('Kelly Grant', 'kelly@example.com', '044 555 555')
def dave = new Person(name: 'Dave Smith', email: 'dave@example.com')
The TupleConstructor annotation gives us the map-based constructor as well as a set of constructors matching the member variables - effectively generating the following constructors:
Person(name)Person(name, email)Person(name, email, mobile)
You can see where the “telescoping” notion comes from - each constructor adds a parameter over the last.
You can configure the annotation to include and exclude specific fields so, when you’re wanting to give it a spin in your own code, check out @groovy.transform.TupleConstructor.
Instance initializer blocks
A stand-alone instance initializer block can be used to provide a base setup for a new instance. This is useful if you need a block of code in order to set default/original values for one or more member variables.
Instance initializer blocks appear inside the class itself, surrounded by curly braces {}:
class Person {
UUID id
String name
{
this.name = 'Anonymous'
this.id = UUID.randomUUID()
}
}
Person paul = new Person()
println paul.dump()
The code above offers nothing over setting the property defaults directly (UUID id = UUID.randomUUID()) - it’s just a simple example. You can use an initializer block or constructors, or both. The initializer block will be called before any constructor(s).
There is a small trap to be wary of when using an instance initializer block - the syntax Groovy uses for passing closures as parameters will cause a failure around initializer blocks in some conditions. The following code is an example of this and won’t run:
class Person {
private Integer id
private String name = 'Anonymous'
{
this.id = UUID.randomUUID()
}
}
Person fred = new Person(name: 'Fred')
In order to get around this, prefix the initializer block with a semicolon (;)1. This stamps a definite statement delimiter against the initializer block:
class Person {
private UUID id
private String name = 'Anonymous'
;{
this.id = UUID.randomUUID()
}
}
Person fred = new Person(name: 'Fred')
println fred.dump()
I’d suggest always using the prefix - it isn’t messy and makes sure that Groovy knows it’s looking at an initializer block.
When and How Things Happen
It can be frustrating when you’ve written a nice looking class but things don’t happen in the manner you intended.
This can be due to the stuff going on behind the scenes that isn’t always immediately obvious.
Let’s take a look at a Person object that uses an initializer block and a constructor as well as provide a field setter and a helper method:
class Person {
private String name
;{
this.name = 'Anonymous'
println "Initialised default name to ${this.name}"
}
public Person(name) {
this.name = name
println "Constructor called with name: ${this.name}"
}
public void setName(name){
this.name = name
println "setName called. Name is now: ${this.name}"
}
public void changeName(name) {
this.name = name
println "changeName called. Name is now: ${this.name}"
}
}
Person fred = new Person('Fred')
println "After instantiation, name is: $fred.name"
fred.name = 'Freda'
fred.changeName 'Frederique'
Running the code above will yield the following output:
Initialised default name to Anonymous
Constructor called with name: Fred
After instantiation, name is: Fred
setName called. Name is now: Freda
changeName called. Name is now: Frederique
From that output we can piece together a basic set of rules that helps us see how things happen:
- The initialiser block is called before the constructor
- Setting a field within object methods (e.g. with
this.name = name) performs a direct change of the field - Setting a field externally (e.g. with
fred.name = 'Freda') causes the setter (setName) to be called
Constructors and instance methods
In the last code example I set the name field directly in the constructor. When looking at the code you may wonder if it isn’t better to have the constructor call the setter (setName) so that I can place any logic around setting the field in a single method. Be careful with this as a more complicated setter may call on other instance fields that have not yet been initialised. Normally, the constructor code is self-contained and would only call other constructors or static methods (we’ll look at these shortly).
However, the changeName method may have been best to use setName rather than changing the field directly. This would allow me to centralise any associated logic.
If you do need to provide some checks or other logic before allowing a field to be set then it might be worth placing this logic in another (private) method that doesn’t change any instance fields directly. To achieve this, the method’s parameters would cover all of the required items for validation (e.g. private String checkName(name, validNameList)) and return the name (if valid) or throw an exception if the check fails. The code might look something like:
private String checkName(name, validNameList) throws IllegalArgumentException {
if (name in validNameList) {
return name
} else {
throw new IllegalArgumentException('The name is not in the list of valid\
names')
}
}
- Thanks to Jochen “blackdrag” Theodorou for his guidance on this↩
79. Class Methods and Variables
In the preceding chapters we looked at instance properties/fields (variables) and instance methods (including constructors) - the components of a class that pertain to individual instantiations of the class. This ensures that each instance works against its own state and doesn’t interfere with other instances. There are many times, however, when we want a class to provide a method that isn’t bound to an instance or when we want a field/property that is used across instances. This is where class methods and class variables are utilised.
So, when to use class methods and variables? There’s a number of answers to this but the following three are the main ones:
- When a single property is best shared across all instances
- This is often the case with configuration properties
- When a method isn’t really specific to an instance
- The method sits nicely with the class’s concepts but doesn’t need to relate directly to each instance
- When the class is really just a library of methods and constants
- Sometimes you just need a set of utility methods
It’s important to note a few things about class methods:
- They can’t access
thisas there’s no underlying instance - They can’t use instance (member) variables
Declaring a field/property or method as static is easy: you just prepend the static modifier. Lets look at an example:
import groovy.json.JsonSlurper
class Configuration {
static String databaseName = ''
static String databasePassword = ''
static String logFile = ''
static loadConfig() {
//This is the config file:
File file = new File('config.json')
//We use JsonSlurper to read a JSON file:
JsonSlurper slurper = new JsonSlurper()
//Now parse the config file
def config = slurper.parse(file)
//We can access the config file elements using dot-point notation:
databaseName = config.database
databasePassword = config.password
logFile = config.log
}
}
Configuration.loadConfig()
println """\
System database: ${Configuration.databaseName}
System database password: ${Configuration.databasePassword}
Log file: ${Configuration.logFile}
"""
The code above provides a common use of static variables as it reads in configuration from a source and makes that
configuration available to the rest of the system. In this case I have chosen to use JSON
notation to store the configuration and the file config.json appears thus:
{
"database": "CorporateData",
"password": "password",
"log": "/tmp/log.txt"
}
In the Configuration class you can see three class variables: databaseName, databasePassword and logFile. These
are accessed via the class, and not an instance, by using the <class name>.<variable name> form: Configuration.databaseName.
I’ve also defined the class method loadConfig and this is accessed through Configuration.loadConfig. You’ll see that
I don’t have to instantiate Configuration by calling new nor do I assign Configuration to a variable - I just access
Configurations’s class members when I need them.
I’ve used the String type for the static variables in Configuration but, should I want to use dynamic types, I just
use static <varName> and don’t need def:
static databaseName = ''
static databasePassword = ''
static logFile = ''
Whilst the Configuration class only defines class methods and variables, classes can have a mix of these elements. The
next example is more complex:
@Grab('org.javamoney:moneta:1.0')
import groovy.transform.ToString
import org.javamoney.moneta.Money
@ToString(includeNames = true)
class SilverCreditCard {
String cardNumber
String cardHolderName
Money balance = Money.of(0, currency)
static String currency = 'AUD'
static Money creditLimit = Money.of(5_000, currency)
static SilverCreditCard applyForAccount(String applicantName, Money totalAss\
ets, Money totalDebts) {
if (totalAssets.subtract(totalDebts).subtract(creditLimit).isPositive())\
{
// TODO: Create a new record in the database etc
return new SilverCreditCard(cardNumber: '0000 1111 2222 3333', cardH\
olderName: applicantName)
} else {
// TODO: Throw an exception - don't just return null
return null
}
}
static SilverCreditCard loadAccount(String cardNumber) {
// TODO: Lookup the number in our database
new SilverCreditCard(cardNumber: cardNumber, cardHolderName: 'Fred Nurk'\
, balance: Money.of(100, currency))
}
Money deposit(Money amt) {
//TODO: Implement
}
Money withdrawl(Money amt) {
//TODO: Implement
}
}
SilverCreditCard yourCard = SilverCreditCard.loadAccount('1234 5678 9876 5432')
println yourCard
SilverCreditCard myCard = SilverCreditCard.applyForAccount('Jim Smith',
Money.of(20_000, SilverCreditCard.currency),
Money.of(10_000, SilverCreditCard.currency))
println myCard
Running this code will cause an error to be displayed but don’t panic - it’s just saying that I haven’t provided a configuration for the Java money classes. It’s all cool and fine for this example.
In the code above I have described a credit card account - the SilverCreditCard. This class uses class
variables to describe the policy for the credit card product (bank speak) and class methods for accessing
individual accounts (after all, you don’t want people to just new CreditCard do you?):
- Class variables:
-
currencyis the currency used by all Silver Credit Cards -
creditLimitis the maximum limit allowed for this type of account
-
- Class methods:
-
applyForAccountis for new customers requesting an account - it would return an instance ofSilverCreditCardbeing the newly created account -
loadAccountis for existing customers wanting to access their account - it would return an instance ofSilverCreditCardwith information loaded from a database
-
The instance elements represent a single customer’s SilverCreditCard account and aid in tracking their account:
- Instance properties:
-
cardNumberis the unique number for the account -
cardHolderNameis the name of the card holder -
balanceis how much they’ve spent on their credit card
-
- Instance methods:
-
depositandwithdrawalwould let the person use their account
-
Static initializer blocks
Classes don’t have a constructor-style approach that you can use to prepare the class variables for use. However, there is a static form of the intializer block:
import groovy.json.JsonSlurper
class Configuration {
static String databaseName = ''
static String databasePassword = ''
static String logFile = ''
static {
//This is the config file:
File file = new File('config.json')
//We use JsonSlurper to read a JSON file:
JsonSlurper slurper = new JsonSlurper()
//Now parse the config file
def config = slurper.parse(file)
//We can access the config file elements using dot-point notation:
databaseName = config.database
databasePassword = config.password
logFile = config.log
}
}
println """\
System database: ${Configuration.databaseName}
System database password: ${Configuration.databasePassword}
Log file: ${Configuration.logFile}
"""
You’ll see that I’ve just moved the earlier loadConfig class method into a static initializer block (static { }).
This is probably a good idea as the initializer block is acted on before the class variables are accessed,
allowing me to make sure that the configuration is ready to go rather than relying on other developers to call loadConfig.
Additionally, the initializer block will only be called once so the config file is only read once
- much more efficient than if loadConfig is called over and over by other code.
- More on this in Access Modifiers↩
80. Final Variables
The final modifier can be prefixed to a class- or instance-variable declaration so as to declare it to be immutable (something that doesn’t change).
Once set, any attempt to change the value will result in a groovy.lang.ReadOnlyPropertyException but you have to be
mindful of a few gotchas, especially with collections and objects - we’ll cover these shortly.
First up, let’s look at the final modifier in action:
class Record {
static final String OWNER
final Date creationDate
}
In the code above I’ve declared one class variable (owner) and one instance variable (creationDate) as final.
You’ll notice that I’ve not actually set the value for these so that’s the next step. I have three options available to
me when setting the value for a final variable:
Option 1: At the point of declaration (class and instance variables):
class Record {
static final String OWNER = 'Secret Corp'
final Date creationDate = new Date()
}
Record myRecord = new Record()
Option 2: In an initializer block (class and instance variables):
class Record {
static final String OWNER
static {
owner = 'Secret Corp'
}
final GregorianCalendar creationDate
;{
creationDate = new GregorianCalendar()
}
}
Record myRecord = new Record()
Option 3: In the constructor (instance variables only):
class Record {
static final String OWNER = 'Secret Corp'
final Calendar creationDate
Record() {
creationDate = new GregorianCalendar()
}
Record(GregorianCalendar created) {
creationDate = created
}
}
Record record1 = new Record()
GregorianCalendar created = new GregorianCalendar(2015, 5, 4)
Record record2 = new Record(created)
Option 1 is usually best for simple assignments (such as a value or a minor expression) and Option 2 is handy if the you need a more complicated expression or set of expressions. The third option is mainly used when the value is passed by the client code into the constructor and then assigned to the instance variable either directly or following some evaluations.
Final fields and the map-like constructor
Just remember that the map-like constructor that comes as a Groovy beans bonus won’t help you with final variables.
The code below won’t work as Groovy is not setting creationDate in the constructor but through the setter after
instantiating the instance:
class Record {
final Date creationDate
}
Record myRecord = new Record(creationDate: new Date())
Final objects
When a variable is marked as final it is the value held by the variable that is immutable. This is fine for primitive values
(such as int) and some of the elementary classes (such as Integer and String) as their underlying value isn’t changeable once
instantiated.
However, if that value points to an object that is mutable (can be changed) then your class might find its variables
being changed by code outside the class. This isn’t a good thing as the class should be managing its own state. Let’s
take a look at how this can happen and how we can stop it.
First up, let’s consider a class FinalReport that is meant to hold a set of Records for archiving purposes. That means
that once a FinalReport has been prepared, we don’t want people tampering with it:
import groovy.transform.ToString
@ToString(includeNames = true)
class FinalReport {
final List records
FinalReport(List records) {
this.records = records
}
}
@ToString(includeNames = true)
class Record {
final Date creationDate = new Date()
String title
String text
Record(String title, String text) {
this.title = title
this.text = text
}
}
def recordSet = [
[ 'Record A', 'This is a record' ] as Record,
[ 'Record B', 'This is another record' ] as Record,
[ 'Record C', 'This is yet another record' ] as Record
]
FinalReport report = new FinalReport(recordSet)
report.records[1].text = 'REDACTED'
println report.records[1]
report.records << new Record('Record Z', 'You just got hacked')
println report
First of all you’ll notice the @ToString(includeNames = true) annotation. This is used to have a toString() method
generated for the class. This is really handy and I provide a description in the Useful Annotations chapter.
When setting up the FinalReport class I dutifully set final List records so that the list of records is final but
two sections of code just blew a hole in my archive-ready report. The first one altered the text of a record in the report:
report.records[1].text = 'REDACTED'
println report.records[1]
The second section of code added a new record to the report:
report.records << new Record('Record Z', 'You just go hacked')
println report
My FinalReport isn’t really very final and is quite open to tampering. This is one reason you write test suites for your code -
to make sure that you haven’t made an incorrect assumption. Let’s take a look at a more locked-down version of the previous
code:
finalimport groovy.transform.ToString
@ToString(includeNames = true)
class FinalReport {
final List records
FinalReport(List records) {
this.records = records.asImmutable()
}
}
@ToString(includeNames = true)
class Record {
final Date creationDate = new Date()
final String title
final String text
Record(String title, String text) {
this.title = title
this.text = text
}
}
def recordSet = [
[ 'Record A', 'This is a record' ] as Record,
[ 'Record B', 'This is another record' ] as Record,
[ 'Record C', 'This is yet another record' ] as Record
]
FinalReport report = new FinalReport(recordSet)
//This will fail with groovy.lang.ReadOnlyPropertyException
try {
report.records[1].text = 'REDACTED'
} catch (ReadOnlyPropertyException e) {
println 'Sorry, you can\'t change a record in a final report'
}
//This will fail with java.lang.UnsupportedOperationException
try {
report.records << new Record('Record Z', 'You just got hacked')
} catch (UnsupportedOperationException e) {
println 'Sorry, you can\'t add a record to a final report'
}
println report
You’ll notice I’ve made a number of changes to really lock things down:
- All of the properties in
Recordare now marked asfinal- This means that
Recordinstances can’t be tampered with post-creation
- This means that
- The
recordsproperty inFinalReportis still marked asfinal(final List records)- This means that the
recordslist can’t just be swapped over for another
- This means that the
- In the
FinalReportconstructor I callasImmutable()against therecordsparameter as this creates a copy of the list and marks it as immutable.- This means that the list can’t have new items added or removed.
My first stab at the code assumed that final List records meant that the list of records couldn’t be changed. This is
true to an extent - once records was assigned an instance of a list it couldn’t be assigned another. However, it didn’t mean
that the items in the list couldn’t be changed or the list have items added/removed. I needed to make sure that each list item
(each being a Record instance) was itself locked down by making all of its properties final. I also needed to lock down
the list being passed to my constructor by using the asImmutable() method to copy the incoming list and stop it from being changed.
The @Immutable annotation
As always, Groovy gives me a very handy approach to locking down my classes so that they’re immutable. The @Immutable
annotation does quite a number of things for me, including:
- Makes properties
final - Sets up a map-based constructor and a tuple constructor (as per
@TupleConstructor) - Ensures that certain types of parameter (such as
Dateand collections) are defensively copied - Prepares a
toStringmethod (as per@ToString)
So here’s how our FinalReport code now looks with the help of @Immutable:
@Immutable annotationimport groovy.transform.Immutable
@Immutable
class FinalReport {
final List records
}
@Immutable
class Record {
Date creationDate = new Date()
String title
String text
}
def recordSet = [
new Record(title: 'Record A', text: 'This is a record'),
new Record(title: 'Record B', text: 'This is another record'),
new Record(title: 'Record C', text: 'This is yet another record')
]
FinalReport report = new FinalReport(recordSet)
//This will fail with groovy.lang.ReadOnlyPropertyException
try {
report.records[1].text = 'REDACTED'
} catch (ReadOnlyPropertyException e) {
println 'Sorry, you can\'t change a record in a final report'
}
//This will fail with java.lang.UnsupportedOperationException
try {
report.records << new Record(title: 'Record Z', text: 'You just got hacked')
} catch (UnsupportedOperationException e) {
println 'Sorry, you can\'t add a record to a final report'
}
println report
You can easily see that the code for my FinalReport and Record classes has been cut right back. This is really
helpful in many situations but @Immutable can’t do everything so make sure you read the documentation.
Copying and cloning
Just wandering a little off the final path, let’s take a quick look at how we could defensively handle mutable objects.
Defensively copying basic objects such as String and Integers is easy as it happens at assignment time:
Integer i = new Integer(10)
Integer j = i
i = 20
assert !i.is(j)
This works because i = 20 causes i to be assigned a new instance of Integer. Similarly, I can copy a list
of numbers quite easily:
def yourList = [2, 4, 6]
def myList = [*yourList]
assert myList == yourList
assert !myList.is(yourList)
This is all reasonably straight-forward as I’m only dealing with basic objects. However, how do I defensively copy
an object that consists of several properties/fields? Earlier, I re-programmed the Record class to make all of the
properties final and this meant that I didn’t really need to defensively copy instances.
Sometimes I don’t get that option, especially for existing or third-party developed classes. In such cases I have a few
options:
- Don’t copy the whole instance, just extract the fields I actually need and copy them
- Create a new instance using the object’s current state as input
- Call the
clone()method if one exists.
The second option is possible if I can use the object’s properties to create another instance via the constructor and/or setters:
import groovy.transform.Canonical
@Canonical
class Assignment {
final String studentName
String answers
}
@Canonical
class SubmissionSystem {
Map submissions = [:]
def submitAssignment(Assignment sub) {
if (!submissions.get(sub.studentName)) {
submissions.put sub.studentName, new Assignment(sub.studentName, sub\
.answers)
}
}
}
Assignment myAssignment = new Assignment('Fred Nurk', 'I have no idea')
SubmissionSystem system = new SubmissionSystem()
system.submitAssignment(myAssignment)
println system
myAssignment.answers = 'A really good set of answers'
println system
In the code above the submitAssignment method calls the Assignment constructor to create a new instance. This helps
make sure that the student can’t mysteriously change their answers after submitting. You can see that it’s a pretty simple
example and a more complex classes will make this very difficult, especially if they have internal state that is hard to
reach.
The third option is to have a class implement the Cloneable interface. If a third-party class provides this then you’re
in luck and can make a copy (clone):
Cloneableimport groovy.transform.Canonical
@Canonical
class Assignment implements Cloneable {
final String studentName
String answers
@Override
protected Assignment clone() throws CloneNotSupportedException {
return new Assignment(studentName, answers)
}
}
@Canonical
class SubmissionSystem {
Map submissions = [:]
def submitAssignment(Assignment sub) {
if (!submissions.get(sub.studentName)) {
submissions.put sub.studentName, sub.clone()
}
}
}
Assignment myAssignment = new Assignment('Fred Nurk', 'I have no idea')
SubmissionSystem system = new SubmissionSystem()
system.submitAssignment(myAssignment)
println system
myAssignment.answers = 'A really good set of answers'
println system
As Assignment provides a clone method we just need to call it and we’re returned a copy for our own use. Naturally,
this doesn’t help us if the author of Assignment doesn’t provide us with a clone method.
Check out the Useful Annotations chapter for the @Canonical annotation.
Final classes and methods
The final modifier can also be used against class and method declarations. We’ll look into this in the
chapter on Final Classes
81. Access Modifiers
Classes are used to embody an abstraction of some real or virtual entity. You can probably guess what a BankAccount or an EmailMessage class embodies. In some cases we might be happy for other code to change a value held in a class instance or to run a method. However, in the case of a BankAccount class we are likely to need some sort of information hiding that moderates what other code is allowed to do:
- The
balancefield shouldn’t be directly accessible. Rather, two methodsdepositandwithdrawalhave to be used. - The
withdrawalmethod should not allow you take take out more money than the current balance.
In Groovy we can implement the business logic just described through the use of Access Modifiers.
Access modifiers are keywords (public, private, protected) that determine if another class can use an element (such as a property, field or method) of the class being described.
-
publicelements can be accessed by all other classes and code -
privateelements are only accessible from within the class itself -
protectedelements are only accessible from subclasses and within their own package
Two things to keep in mind:
- Groovy applies
publicby default so you don’t need to explicitly declare anything aspublic. - Providing an access modifier means that
defisn’t needed when you’re using dynamic types:-
private balance = 0is preferred overprivate def balance = 0 - Similarly for methods:
private applyAccountCharges() {...}rather thanprivate def applyAccountCharges() {...}
-
For our BankAccount class we can make the balance field private:
class BankAccount {
private Integer balance = 0
Integer withdrawal(amount) {
if (amount > balance) {
throw new Exception('Insufficient balance')
}
balance -= amount
}
Integer deposit(amount) {
balance += amount
}
}
def acct = new BankAccount()
acct.deposit(100)
acct.withdrawal(150)
In the example above I set the balance field to private and then provide two public methods to allow for other code to perform a deposit or a withdrawal. The latter method even throws an exception if you try to take out too much.
Now here’s “the rub”. Groovy doesn’t actually enforce the access modifier. That means that, given my BankAccount class I could still write acct.balance = 1_000_000 and access the field directly. A Python programmer might shrug at this and state that it’s a matter of respecting the original programmer’s intention. A Java programmer might be shocked that Groovy doesn’t throw an exception or an error.
I’m usually pretty happy with the Python approach but if it was really a concern I could add the following method to my BankAccount class, as seen in the next example:
class BankAccount {
private Integer balance = 0
Integer withdrawal(amount) {
if (amount > balance) {
throw new Exception('Insufficient balance')
}
balance -= amount
}
Integer deposit(amount) {
balance += amount
}
private setBalance(amount) {
}
}
def acct = new BankAccount(balance: 200)
assert acct.balance == 0
def acct2 = new BankAccount()
acct.balance = 100
assert acct2.balance == 0
Groovy generates setters and getters for properties but I need provide them for fields. In the case of the balance field, the setter method named setBalance is actually called when I do something like acct.balance = 1_000_000. Knowing this, I overrode the setter Groovy would have created with my own version that does nothing and I also used the private modifier. This does two things:
- The
privatemodifier reinforces to other developers that they should not try to directly change the value ofbalance - If the other developers just don’t listen then I ignore their attempt to change the
balance.
Whilst my empty setBalance method helps prove a point, having to do that too often will reduce readability and annoy me with having to write lots of vacant code to protect my code from others who don’t listen. Essentially I take the approach that developers are (usually) sensible people that understand what they’re being asked not to fiddle with. If they fiddle then they can expect a weird outcome. So feel free to write a method with the signature private doSomethingEvil(), add some code that deletes all files and see who doesn’t pay attention :-)
Applying Access Modifiers
The following access modifiers are available:
- Classes:
publicprotected
- Fields
publicprivateprotected
- Methods
publicprivateprotected
Remember, you don’t need to explicitly declare an element as public as this is the default.
- If you want to really get into this topic, start with http://docs.oracle.com/javase/tutorial/essential/environment/security.html↩
82. Useful Annotations
Groovy comes with a number of handy notations that let you easily customise a class without doing the hard work yourself.
This chapter will take a brief look at some of the handier annotations in the
groovy.tranform package.
You can even write your own annotations but that’s something for another book…
ToString
The toString() method is used to provide a “human readable” representation of the object. It’s available on all
objects and you can override it but you can also just use the ToString annotation to make this even easier:
@groovy.transform.ToString
class Person {
def name
def email
def mobile
Person(name) {
this.name = name
}
}
def astrid = new Person('Astrid Smithson')
println astrid
ToString takes a few options:
-
@ToString(includeNames=true): will add in the property name, prefixing the property value -
@ToString(includeFields=true): by default, properties are used but this adds in fields
You can use several options at once:
@groovy.transform.ToString(includeNames=true, includeFields=true)
class Person {
def name
private email
private mobile
Person(name) {
this.name = name
}
}
def astrid = new Person('Astrid Smithson')
println astrid
EqualsAndHashCode
Determining if two instances are equal is something you have to add in yourself. Groovy will agree that two variables are the same if they point to the same instance of a class:
def agentSmith1 = new Person(id: 411, name: 'Agent Smith')
def agentSmith2 = agentSmith1
assert agentSmith1 == agentSmith2
However, this isn’t Groovy being clever, it’s just seeing that agentSmith1 and agentSmith2 point to the same thing.
In the code below you’ll see that a Person instance with the same id and name as another Person instance doesn’t
automatically equate to them being equal:
class Person {
def id
def name
}
def agentSmith1 = new Person(id: 411, name: 'Agent Smith')
def agentSmith2 = agentSmith1
def agentSmith3 = new Person(id: 411, name: 'Agent Smith')
assert agentSmith1 == agentSmith2
//This will fail
assert agentSmith2 == agentSmith3
By using the EqualsAndHashCode annotation I can tell Groovy that two instances of Person with the same id are actually
equal:
@groovy.transform.EqualsAndHashCode(includes='id')
class Person {
def id
def name
}
def agentSmith = new Person(id: 414, name: 'Agent Smith')
def agentSmith2 = new Person(id: 414, name: 'Agent X')
assert agentSmith == agentSmith2
Equality is not always easy to determine and will depend on the context in which you’re developing - my example above
is likely to cause some debate as to if matching only on id is enough. The EqualsAndHashCode annotation has a few options that are worth digging into.
Canonical
The Canonical annotation brings together functionality from a suite of other annotations:
ToStringEqualsAndHashCodeTupleConstructor
We’ve looked at these previously so let’s jump to an example:
@groovy.transform.Canonical
class Person {
def name
def email
def mobile
}
Person phil = new Person('Phil', '041414141')
println phil
This saves you from needing to stack your annotations:
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import groovy.transform.TupleConstructor
@ToString
@EqualsAndHashCode
@TupleConstructor
class Person {
def name
def email
def mobile
}
Person phil = new Person('Phil', '041414141')
println phil
Immutable
The Immutable annotation provides similar features as Canonical but locks down new instances so that they can’t be changed after creation.
Immutable objects can be very useful in systems using parallel processing or caching.
@groovy.transform.Immutable
class Person {
String name
String email
String mobile
}
def krusty = new Person(name: 'Krusty')
krusty.email = 'heyhey@example.com'
The code above will fail on krusty.email = 'heyhey@example.com' as the Immutable annotation marks the email field as final.
83. Let Go of That Instance!
Sometimes your variable (e.g. a class instance) can end up holding a large amount of data. For example, you might have placed the text of the complete works of Shakespeare into a property. The JVM performs a process called Garbage Collection so as to clean up data that you are no longer using. By assigning a variable to null we flag that the data previously held by the variable is no longer needed:
myInstance = null
For small Groovy scripts this may never be an issue but, for long running applications, data hanging around and not being used can start to soak up a serious amount of resources, especially memory. Once you’ve finished with a variable it’s worth assigning its value to null to let the JVM know you don’t need it anymore.
You don’t always need to explicitly set variables to null - this would make your code far less readable. The JVM knows that once a variable is no longer in scope, it’s no longer accessible and, thus, is no longer needed. This means that if you have a variable declared inside a method, its value is no longer needed once the method has completed. The same goes for variables declared within scopes such as loops. This can get a bit tricky when regarding closures and object references so it’s not all plain sailing.
There is an important caveat: if multiple variables refer to the same data then the JVM can only release resources once all references have “unlatched”. Let’s examine this in the code below:
class SampleText {
def text
}
def shakespeare = new SampleText(text: 'It was the best of times....')
def marlow = shakespeare
shakespeare = null
println marlow.text
I’ve defined a variable (shakespeare) to hold a new instance of the SampleText class and then said that another variable (marlow) points to that instance of SampleText. My call to marlow.text will still work despite my setting shakespeare to null. In this case we say that “marlow still holds a reference to the SampleText instance”. This means that the JVM can’t release the resources held by the instance until all references are set to null. I need to set marlow to null to completely release the resources.
There’s a lot more to garbage collection than I want to cover here but if you spend a few moments searching “Java Garbage Collection” you’ll be able to delve deep into the topic.
84. Enums
Enums are a special type of class that represent an enumerated type
- they’re used to define one or more constants. Sometimes people use strings for this but they aren’t always the best
option for checking identifiers and enums are a nicer approach to declaring a set of constants than having lots of
static final variables in a class.
Booleans represent an enumerated type1 that can be true or false but,
whilst these are English-language keywords, the boolean value true isn’t a string representing the text “true”:
String t = 'true'
Boolean b = true
//This will fail
assert t == b
So let’s take a look at a very simple enum:
enum Months {
JAN,
FEB,
MAR,
APR,
MAY,
JUN,
JUL,
AUG,
SEP,
OCT,
NOV,
DEC
}
// enums help comparisons
assert Months.JAN != Months.DEC
assert Months.APR == Months.APR
// enums aren't text strings
assert Months.OCT != 'Oct'
// Assign a variable to be the value of an enum constant
Months myMonth = Months.AUG
// Iterate through the months
for (month in Months) {
println month
}
//The values method returns a list of the constants in the enum
println Months.values()
First up you’ll see that the enum keyword replaces class. Next, I’ve named the enum Months and provided a set
of three-letter constants for each of the months in a year - these are the enum’s constants. The enum’s constants are
accessed using the same approach we use for static variables: Months.JAN. Essentially, the enum’s constant is much the
same as a class’s static final variables but the ability to loop through the enum with for (month in Months) {}
marks enums as managing a set of constants rather than individual variables/constants.
In the line Months myMonth = Months.AUG you’ll see that enums define types but in a manner different to that we saw in classes.
The myMonth variable is of the enum type Months but we don’t create the instance by calling Months myMonth = new Months().
Instead, we assign myMonth to the value of one of the constants as AUG is a constant of type Months.
One more point before moving on, the enum constants don’t have to be declared in upper-case, that’s just the usual approach
and mirrors how we declared constants using the static final modifiers. It’s a just a standard approach to style rather than
required by the language.
Let’s take a look at another example - this time I’ll create a Gender enum and use it in my Person class:
import groovy.transform.ToString
enum Gender {
MALE,
FEMALE
}
@ToString
class Person {
String name
String email
String mobile
Gender gender
}
Person jane = new Person(name: 'Jane', gender: Gender.FEMALE)
println jane
There’s probably nothing too new in that example but it helps us take a next step - to give the Gender enum constants a value:
import groovy.transform.ToString
enum Gender {
MALE('male'),
FEMALE('female')
final String value
Gender(value) {
this.value = value
}
@Override
String toString() {
value
}
}
@ToString
class Person {
String name
String email
String mobile
Gender gender
}
Person jane = new Person(name: 'Jane', gender: Gender.FEMALE)
println jane
Describing that last example can get a litle tricky so I’m going to step through it. First of all, I start the enum declaration as you’d expect:
enum Gender {
Then I list the enum’s constants but they look a little odd. In fact they look like constructor calls:
MALE('male'),
FEMALE('female')
Remember how we don’t call new Gender()? That’s because MALE is a analagous to single static instance of the Gender enum and the
call MALE('male') is instantiating MALE via the constructor. Importantly, enum constructors are called internally
by the enum and not from any external source - they’re private to the enum. The constructor is called once for each constant.
The next part of the Gender enum, as listed below, declares a member variable (value) and the constructor sets the
variable based on the incoming parameter:
final String value
Gender(value) {
this.value = value
}
I have declared value to be final as I don’t want others to change it. Whilst I could drop the final modifier
it’s not a great idea as enums are generally seen as a constant construct.
Lastly, I provide a toString method that helps us when displaying an enum constant:
@Override
String toString() {
value
}
The order within an enum is important and you must put the list of constants before any other items. I generally prefer to lay them out as follows:
- Constants
- Member variables
- Constructors
- Methods
Enums describe not just a set of constants but their order. Groovy provides the built-in next and previous methods
that help step through the constants in an enum. Let’s look at a school grades enum and the result of calling next on each constant:
enum Grades {
FAIL,
PASS,
CREDIT
}
println Grades.FAIL.next()
println Grades.PASS.next()
println Grades.CREDIT.next()
Running this script will yield:
PASS
CREDIT
FAIL
Unfortunately, according to Groovy, the next highest grade after CREDIT is FAIL - the next function just loops back to the first constant.
This next version will fix that by overriding the default behaviours for next and previous:
enum Grades {
FAIL,
PASS,
CREDIT
def next() {
switch (this) {
case FAIL:
return PASS
case PASS:
return CREDIT
case CREDIT:
return CREDIT
}
}
def previous() {
switch (this) {
case FAIL:
return FAIL
case PASS:
return FAIL
case CREDIT:
return PASS
}
}
}
println Grades.FAIL.next()
println Grades.PASS.next()
println Grades.CREDIT.next()
println Grades.FAIL.previous()
println Grades.PASS.previous()
println Grades.CREDIT.previous()
This approach can be really useful when dealing with constants that can be escalated. Think about examples such as
a Priority enum with constants such as LOW, MEDIUM and HIGH or a DeliveryTime enum with NORMAL, PRIORITY and
EXPRESS.
- However, Boolean is a class and extends
java.lang.Object. Enums implcitly extendjava.lang.Enum.↩
85. The synchronized modifier
The synchronized keyword is used in concurrent environments in which multiple threads are operating and there’s potential that code running on different threads are interacting on the same section of code. The synchronized modifier is used to indicate that the associated element can only be accessed by one caller at a time. In the code below you’ll see the various applications of synchronized available:
synchronizedclass PrizePool {
synchronized amount
static synchronized pools
synchronized calculateTotalPool() {
//synchronized code
}
def calculateWinnings() {
//non-synchronized code
synchronized (this) {
//synchronized code
}
//non-synchronized code
}
static synchronized removePool() {
//synchronized code
}
static addPool() {
//non-synchronized code
synchronized (PrizePool) {
//synchronized code
}
//non-synchronized code
}
}
- First of all, member and static variables can be marked as
synchronized- only one thread can read/write the variable at a time. - Instance and class methods can also be marked as
synchronized. Once called by one thread, any other threads are prevented from accessing the method until the first thread’s call has completed. - One or more blocks within an instance or class method can be
synchronized1:- Within an instance method the block is prefixed with
synchronized (this)- note the use of an object instance (this) - For a class method, the block is prefixed with
synchronized (PrizePool)- note the use of a class name (PrizePool)
- Within an instance method the block is prefixed with
A good starting place for understanding this topic is the Concurrency section of The Java Tutorials. Additionally, the GPars Framework provides a library to assist you in writing concurrent/parallel Groovy and Java code.
- Whilst the example blocks use
thisinstance and thePrizePoolclass, you can use other instances and classes.↩