May 22, 2014

Groovy Goodness: Implementing Traits at Runtime

Groovy 2.3 introduced traits as a new language construct. We can apply traits when we write new classes in Groovy using the implements keyword. But we can also apply traits dynamically at runtime. For example if we want to apply a trait to an already existing class and we cannot change the source code of that class.

To apply a single trait we can use the as keyword. The original object is then coerced into the trait instance. In the following sample we define a new trait Name with a couple of properties and a method. Also we have a User class we want to apply this trait to. After we have applied the trait we can access the properties of the Name trait and invoke the method getFullName, actually we get a new object instance with the capabilities of the User class and Name trait:

trait Name {
    String salutation, firstName, lastName

    String getFullName() {
        [salutation, firstName, lastName].join(' ')

class User {
    String username, password   

def user = new User() as Name
user.with {
    salutation = 'Mr.'
    firstName = 'Hubert'
    lastName = 'Klein Ikkink'
    username = 'mrhaki'
    password = '*****'

assert user.fullName == 'Mr. Hubert Klein Ikkink'
assert user.username == 'mrhaki'

To apply multiple traits to an object we must use the method withTraits on an object. We can use one or more traits as arguments to this method. In the following sample we apply the traits Id, Version and Active with some properties and methods to an instance of the Person class. The withTraits methods return a new object instance which has all the properties and methods of the original class and the traits.

trait Id {
    Long id

trait Version {
    Long version = 0

trait Active {
    Date from = new Date()
    Date to = null

    boolean isActive() {
        final Date now = new Date()
        from < now && (!to || to > now)

class Person {
    String username

def person = new Person(username: 'mrhaki')
def domainPerson = person.withTraits Id, Version, Active = 1

assert domainPerson.username == 'mrhaki'
assert == 1
assert domainPerson.version == 0

Code written with Groovy 2.3.1.