Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Groovy Goodness: Make Class Cloneable With @Autoclone

DZone's Guide to

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
Free Resource

Learn how to build modern digital experience apps with Crafter CMS. Download this eBook now. Brought to you in partnership with Crafter Software

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.

Crafter is a modern CMS platform for building modern websites and content-rich digital experiences. Download this eBook now. Brought to you in partnership with Crafter Software.

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.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}