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 class we might describe an Animal in terms of its features (diet, distribution) and then enhance the Animal with traits such as Hopping (for a wallaby) or Climbing (for a koala) or both (for a tree kangaroo).
  • A BankAccount class would have its properties (balance, accountHolder) but different accounts provides different abilities (or a combination thereof) such as Deposit, Overdraft or CurrencyConversion.

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:

A basic trait example
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 trait keyword 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 Running trait you can see that there’s only one method declared: startEvent().

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.

  1. We’ll look into this further in the chapter on Inheritance.