95. Traits
Where classes can be used to describe real or virtual “things”, traits provide a construct for describing an ability or a set of related abilities. Let’s consider two contexts in which we might use traits:
- Using a
classwe might describe anAnimalin terms of its features (diet,distribution) and then enhance theAnimalwith traits such asHopping(for a wallaby) orClimbing(for a koala) or both (for a tree kangaroo). - A
BankAccountclass would have its properties (balance,accountHolder) but different accounts provides different abilities (or a combination thereof) such asDeposit,OverdraftorCurrencyConversion.
It’s sometimes difficult to determine where a set of methods might instead be better grouped as a trait but the following may help your decision making:
- If the methods describe an ability or feature that is common to a variety of classes they’re a good candidate for a trait
- Even if this is at the abstract level and would need to be more specific for subtypes1 - a wallaby hops differently to a frog.
- Where the various methods manipulate properties or fields that are otherwise not accessed or only read by the class.
Let’s take a look at an example trait:
trait Running {
void startEvent() {
println 'Ready, set, go!'
}
}
class SportingEvent implements Running {
String name
}
SportingEvent stawellGift = new SportingEvent(name: 'The Stawell Gift')
println "Welcome to $stawellGift.name"
stawellGift.startEvent()
This type of layout is much as we saw for class and interface definitions:
- The declaration starts with the
traitkeyword followed by the trait’s name - We use the CamelCase format for the trait’s name - as we did for classes and interfaces
- Similar to a class, the body of the trait then provides the required properties, fields and methods
- In the
Runningtrait you can see that there’s only one method declared:startEvent().
- In the
When it comes to use the trait in a class, the implements key is used: class SportingEvent implements Running. The class declaration can implement zero or more traits in the same way it can implement zero or more interfaces. In fact, a class declaration can implement a mix of traits and interfaces as can be seen in the snippet below:
class SportingEvent implements SafetyCheck, Running, Swimming {...}
At a guess, the Running and Swimming elements are traits and the SafetyCheck element could be a trait or an interface - we’d need to check the source code or groovydoc.
- We’ll look into this further in the chapter on Inheritance.↩