@Documented
@Retention(value=RUNTIME)
@Target(value=TYPE)
public @interface AutoClone
Cloneable
classes.
The @AutoClone
annotation instructs the compiler to execute an
AST transformation which adds a public clone()
method and adds
Cloneable
to the interfaces which the class implements.
Because the JVM doesn't have a one-size fits all cloning strategy, several
customizations exist for the cloning implementation. By default, the clone()
method will call super.clone()
before calling clone()
on each
Cloneable
property of the class.
Example usage:
import groovy.transform.AutoClone
@AutoClone
class Person {
String first, last
List favItems
Date since
}
Which will create a class of the following form:
class Person implements Cloneable { ... public Object clone() throws CloneNotSupportedException { Object result = super.clone() result.favItems = favItems.clone() result.since = since.clone() return result } ... }Which can be used as follows:
def p = new Person(first:'John', last:'Smith', favItems:['ipod', 'shiraz'], since:new Date()) def p2 = p.clone() assert p instanceof Cloneable assert p.favItems instanceof Cloneable assert p.since instanceof Cloneable assert !(p.first instanceof Cloneable) assert !p.is(p2) assert !p.favItems.is(p2.favItems) assert !p.since.is(p2.since) assert p.first.is(p2.first)In the above example,
super.clone()
is called which in this case
calls clone()
from java.lang.Object
. This does a bit-wise
copy of all the properties (references and primitive values). Properties
like first
has type String
which is not Cloneable
so it is left as the bit-wise copy. Both Date
and ArrayList
are Cloneable
so the clone()
method on each of those properties
will be called. For the list, a shallow copy is made during its clone()
method.
If your classes require deep cloning, it is up to you to provide the appropriate
deep cloning logic in the respective clone()
method for your class.
If one of your properties contains an object that doesn't support cloning
or attempts deep copying of a data structure containing an object that
doesn't support cloning, then a CloneNotSupportedException
may occur
at runtime.
Another popular cloning strategy is known as the copy constructor pattern.
If any of your fields are final
and Cloneable
you should set
style=COPY_CONSTRUCTOR
which will then use the copy constructor pattern.
Here is an example making use of the copy constructor pattern:
import groovy.transform.AutoClone import static groovy.transform.AutoCloneStyle.*Which will create classes of the following form:@AutoClone(style=COPY_CONSTRUCTOR)
class Person { final String first, last final Date birthday }@AutoClone(style=COPY_CONSTRUCTOR)
class Customer extends Person { final int numPurchases final List favItems }
class Person implements Cloneable { ... protected Person(Person other) throws CloneNotSupportedException { first = other.first last = other.last birthday = other.birthday.clone() } public Object clone() throws CloneNotSupportedException { return new Person(this) } ... } class Customer extends Person { ... protected Customer(Customer other) throws CloneNotSupportedException { super(other) numPurchases = other.numPurchases favItems = other.favItems.clone() } public Object clone() throws CloneNotSupportedException { return new Customer(this) } ... }If you use this style on a child class, the parent class must also have a copy constructor (created using this annotation or by hand). This approach can be slightly slower than the traditional cloning approach but the
Cloneable
fields of your class can be final.
As a final example, if your class already implements the Serializable
or Externalizable
interface, you can choose the following cloning style:
@AutoClone(style=SERIALIZATION)
class Person implements Serializable {
String first, last
Date birthday
}
which outputs a class with the following form:
class Person implements Cloneable, Serializable { ... Object clone() throws CloneNotSupportedException { def baos = new ByteArrayOutputStream() baos.withObjectOutputStream{ it.writeObject(this) } def bais = new ByteArrayInputStream(baos.toByteArray()) bais.withObjectInputStream(getClass().classLoader){ it.readObject() } } ... }This will output an error if your class doesn't implement one of
Serializable
or Externalizable
, will typically be
significantly slower than the other approaches, also doesn't
allow fields to be final, will take up more memory as even immutable classes
like String will be cloned but does have the advantage that it performs
deep cloning automatically.
Further references on cloning:
AutoCloneStyle
,
AutoExternalize
Modifier and Type | Optional Element and Description |
---|---|
java.lang.String[] |
excludes
Comma separated list of property names to exclude from cloning.
|
boolean |
includeFields
Include fields as well as properties when cloning.
|
AutoCloneStyle |
style
Style to use when cloning.
|
public abstract java.lang.String[] excludes
public abstract boolean includeFields
public abstract AutoCloneStyle style