Skip to content
AgileLogics
6 min read

Traits

Prerequisites: Inheritance and interfaces

A trait is a reusable piece of behavior — methods and state — that you can mix into any class. Think of it as an interface that can carry an implementation and fields.

trait Greeter {
    String greet() { "Hello, I'm ${name}" }
    abstract String getName()
}

trait Timestamped {
    List<String> history = []
    void log(String event) { history << "${history.size() + 1}: $event" }
}

class Robot implements Greeter, Timestamped {
    String name
}

def r = new Robot(name: 'R2')
println r.greet()
r.log('powered on')
r.log('beeped')
println r.history
println r instanceof Greeter
Hello, I'm R2
[1: powered on, 2: beeped]
true
  • A class implements traits just like interfaces — as many as it wants.
  • Traits can declare state (history) — each object gets its own.
  • An abstract method lets a trait require something from the class.

Conflicts

trait Walker { String move() { 'walking' } }
trait Swimmer { String move() { 'swimming' } }

class Duck implements Walker, Swimmer {}
class Penguin implements Swimmer, Walker {
    String move() { "${Swimmer.super.move()} and ${Walker.super.move()}" }
}

println new Duck().move()
println new Penguin().move()
swimming
swimming and walking

When two traits define the same method, the last one declared wins (Swimmer for Duck). To combine them, override the method and call TraitName.super.method() explicitly.

Adding traits at runtime

trait Loud {
    abstract String getText()
    String shout() { text.toUpperCase() + '!' }
}

class Message {
    String text
}

def msg = new Message(text: 'deploy done')
def loud = msg.withTraits(Loud)
println loud.shout()
println loud instanceof Loud
println msg instanceof Loud
DEPLOY DONE!
true
false

withTraits returns a new proxy object that has the extra behavior and forwards everything else (like getText()) to the original; msg itself is unchanged.

Traits vs. inheritance

Use When
Inheritance (extends) A true "is-a" relationship with one parent
Interface A contract with no shared implementation
Trait Shared, reusable behavior across unrelated classes

Traits are compiled to interfaces plus helper classes, so they work with Java code that expects the interface.