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(" ")