Traits

trait Greetable {
    abstract String getName()                    // required by the implementing class
    String greet() { "Hello, I'm ${name}" }
}

trait Timestamped {
    Date createdAt = new Date()                  // traits can have state
    String age() { "${(new Date().time - createdAt.time) / 1000}s old" }
}

trait Loggable {
    void log(String msg) { println "[${getClass().simpleName}] $msg" }
}

class User implements Greetable, Timestamped, Loggable {
    String name
}

def u = new User(name: "Ana")
println u.greet()
u.log("created at ${u.createdAt}")

18.1 Trait Features

Traits can extend other traits (trait A extends B) and implement interfaces. @SelfType constrains which classes can use a trait.

trait Counter {
    private int count = 0                       // private trait state
    int increment() { ++count }
    static String kind() { "counter" }          // static methods (with caveats)
}

trait Flying { String move() { "fly" } }
trait Swimming { String move() { "swim" } }
class Duck implements Flying, Swimming {        // conflict: the last declared trait wins (Swimming)
    String move() { Flying.super.move() + "+" + Swimming.super.move() }   // explicit resolution
}
println new Duck().move()

// Runtime trait application
trait Extra { String extra() { "runtime trait" } }
def obj = new Object().withTraits(Extra)
println obj.extra()
def duck = new Duck() as Extra

// SAM-type traits: a closure can be coerced to a trait with a single abstract method
trait Validator { abstract boolean validate(String s); boolean check(String s) { validate(s) } }
Validator nonEmpty = { it?.trim() }
println nonEmpty.check("  ")