Over a million developers have joined DZone.

Groovy Goodness: Make Class Cloneable With @Autoclone

We have a look at how the @AutoClone annotation can be used to add a .clone() method to your classes. What can be done with this?

· Web Dev Zone

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

Groovy has many AST annotations that add code to our class (the Abstract Syntax Tree - AST) before it is compiled. So the compiled class file contains the code added by the AST annotation. With the @AutoClone annotation, a clone method is added and the class implements the Cloneable interface. We have different strategies to choose from to support cloning for our class.

The default strategy is to invoke super.clone() in the generated clone method. The next statements will deep copy the properties (and optional fields) from our class. If one of the properties cannot be cloned, an exception is thrown. In the following example code snippet, we apply the @AutoClone annotation to the classes Course and Teacher:

import groovy.transform.AutoClone

@AutoClone
class Course {
 String name
 Date date
 Teacher teacher
}

@AutoClone
class Teacher {
 String name
}

def mrhaki =
 new Teacher(name: 'mrhaki')

def course = 
 new Course(
 name: 'Groovy 101',
 date: new Date() + 10,
 teacher: mrhaki)

// We make a deep copy.
def secondCourse = course.clone()
assert secondCourse != course
assert !secondCourse.is(course)
assert secondCourse.teacher != course.teacher

// Change property on cloned instance.
secondCourse.name = 'Groovy 101 2nd edition'

assert secondCourse.name == 'Groovy 101 2nd edition'
assert course.name == 'Groovy 101'

We can use the excludes annotation attribute to give a list of properties that must not be cloned:

import groovy.transform.AutoClone

// Do not clone the teacher property.
@AutoClone(excludes = ['teacher'])
class Course {
 String name
 Date date
 Teacher teacher
}

@AutoClone()
class Teacher {
 String name
}

def mrhaki =
 new Teacher(name: 'mrhaki')

def course = 
 new Course(
 name: 'Groovy 101',
 date: new Date() + 10,
 teacher: mrhaki)

// We make a deep copy.
def secondCourse = course.clone()
assert secondCourse != course
assert !secondCourse.is(course)
// Only the teacher property is
// a shallow copy.
assert secondCourse.teacher == mrhaki
assert secondCourse.teacher.is(mrhaki)

// Change property on teacher property on cloned instance.
secondCourse.teacher.name = 'hubert'

assert secondCourse.teacher.name == 'hubert'
assert course.teacher.name == 'hubert'

To include fields as well as properties, we must set the annotation attribute includeFields to true.

If we want to invoke the default constructor of our class in the clone method, we must use the clone style AutoCloneStyle.SIMPLE. In the generated clone method, the constructor is invoked followed by copying the properties:

import groovy.transform.AutoClone
import static groovy.transform.AutoCloneStyle.SIMPLE

@AutoClone(style = SIMPLE)
class Course {
 String name
 Date date
 Teacher teacher

 static int counter

 Course() {
 counter++
 }
}

@AutoClone(style = SIMPLE)
class Teacher {
 String name

 static int counter

 Teacher() {
 counter++
 }
}

def mrhaki =
 new Teacher(name: 'mrhaki')

def course = 
 new Course(
 name: 'Groovy 101',
 date: new Date() + 10,
 teacher: mrhaki)

def otherCourse = course.clone()

// Constructor is invoked twice:
// once by ourselves to create a 
// course, the other by the clone()
// method added by @AutoClone.
assert course.counter == 2
assert course.teacher.counter == 2

The last clone style we can choose is AutoCloneStyle.COPY_CONSTRUCTOR. This time, the annotation will add a protected constructor that takes another object of the same type as argument. This new constructor is used in the generated clone method. This style is useful if we have final read-only properties that can only be set via the constructor:

import groovy.transform.AutoClone
import static groovy.transform.AutoCloneStyle.COPY_CONSTRUCTOR

@AutoClone(style = COPY_CONSTRUCTOR)
class Course {
 final String name
 final Date date
 final Teacher teacher

 Course(
 final String name,
 final Date date,
 final Teacher teacher) {

 this.name = name
 this.date = date
 this.teacher = teacher
 }
}

@AutoClone(style = COPY_CONSTRUCTOR)
class Teacher {
 final String name

 Teacher(final String name) {
 this.name = name
 }
}

def mrhaki =
 new Teacher('mrhaki')

def course = 
 new Course(
 'Groovy 101',
 new Date() + 10,
 mrhaki)

def secondCourse = course.clone()
assert secondCourse != course
assert !secondCourse.is(course)
assert secondCourse.teacher != mrhaki
assert !secondCourse.teacher.is(mrhaki)

This annotation was already available since Groovy 1.8.

Written with Groovy 2.4.6.

Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:
annotation ,method ,constructor ,groovy ,class

Published at DZone with permission of Hubert Klein Ikkink, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}