Skip to content
Traits in Depth
step 1/5

Reading — step 1 of 5

Learn

~1 min readTraits and DSL Composition

Groovy traits are like Java interfaces but with implementation. Like Scala traits, like Kotlin interfaces with default methods, like Ruby modules.

trait Greetable {
    abstract String name()         // must be implemented

    String hello() {
        "Hello, ${name()}"
    }
}

class Person implements Greetable {
    String n
    Person(String n) { this.n = n }
    String name() { n }
}

new Person("Ada").hello()    // "Hello, Ada"

Multiple traits — diamond OK:

trait Logging {
    void log(String msg) { println "[${this.class.simpleName}] $msg" }
}
trait Caching {
    Map cache = [:]
    def cached(String key, Closure compute) {
        cache.computeIfAbsent(key, { compute() })
    }
}

class Service implements Logging, Caching {
    String process(String input) {
        log("processing $input")
        cached(input, { input.toUpperCase() })
    }
}

Groovy resolves diamond conflicts in implementation order (last-trait-wins).

Stateful traits — they CAN have fields (unlike Java interfaces):

trait Counter {
    int count = 0
    void increment() { count++ }
}

The field is added to each implementing class.

Stackable trait pattern — combine multiple traits' behavior on a method:

trait Filtered {
    String process(String s) {
        s.replace("bad", "***")
    }
}
trait Logged {
    String process(String s) {
        println "processing: $s"
        Filtered.super.process(s)    // or super.process(s) — depending on order
    }
}

class Filter implements Filtered, Logged {}

Traits vs categories:

  • Traits: declared at class level, type-safe, mixed in at compile time
  • Categories (use(Category)): scope-limited, dynamic, similar to monkey patching

For most modern Groovy code, prefer traits — they're cleaner.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…