101. Implementing multiple traits
As mentioned earlier, a class can implement more than one trait. This is straight-forward if the traits don’t intersect in terms of members (properties/fields/methods), as is the case in the example below:
trait Hopping {
String hop() { 'I am hopping' }
}
trait Climbing {
String climb() { 'I am climbing' }
}
class TreeKangaroo implements Hopping, Climbing {}
TreeKangaroo lumholtz = new TreeKangaroo()
println lumholtz.hop()
println lumholtz.climb()
There’s nothing too difficult in the TreeKangaroo example but what if the traits had methods with the same name? Let’s belabour the sporting example one last time!
I believe that the Triathlon consists of three parts - Running, Swimming, and Riding - and the example below sets up each of these as traits that implement the same interface:
interface Event {
void startEvent()
}
trait Running implements Event {
@Override
void startEvent() {
println 'Start the running event'
}
}
trait Swimming implements Event {
@Override
void startEvent() {
println 'Start the swimming event'
}
}
trait Riding implements Event {
@Override
void startEvent() {
println 'Start the riding event'
}
}
class Triathlon implements Running, Swimming, Riding {
}
Triathlon competition = new Triathlon()
competition.startEvent()
It’s easy to see that the Triathlon class now has three possibilities when competition.startEvent() is called. In this scenario, Groovy will use the trait that was declared last - Riding. I’ve listed my traits in the order I want to run the events but I really do want to run all three events.
Groovy lets me manually determine how the colliding trait members will be treated. First of all, the Triathlon class will need to provide its own void startEvent() method. Then each trait’s startEvent() method will need to be called using the <TraitName>.super. prefix - e.g. Running.super.startEvent().
In the improved Triathlon example below you’ll notice that I’ve decided to implement the Event interface as a triathlon is an event consisting of three events:
class Triathlon implements Event, Running, Swimming, Riding {
@Override
void startEvent() {
Running.super.startEvent()
Swimming.super.startEvent()
Riding.super.startEvent()
}
}
This will now give me the three-stage event I was after.
Before leaving this topic, there are some things to note about the example:
- The traits don’t have to implement the same interface, there just needs to be a collision in one or more of the trait members
- I didn’t have to override the
startEventmethod - I could have used any name - but that would return to Groovy’s default of using thestartEventmethod of the last trait declared - Use of the
<TraitName>.super.prefix doesn’t have to occur just in cases of a collision - you may just use it to clarify a section of code. - Concepts such as overriding and
superwill be covered more fully in the chapter on Inheritance.