Adding the cloning functionality to Groovy Beans

There are several strategies to clone an object in Java. To clone an object means the ability to create an object with the same state as the original object.

A widely used strategy to clone an object is for the class to be cloned to implement the Cloneable interface, and implement a method, clone, in which the cloning code is executed. Naturally, Groovy supports this semantic but makes it even easier to implement with the @AutoClone annotation, which will be demonstrated in this recipe.

How to do it...

The following steps will show the power of the @AutoClone annotation:

  1. Let's define an object Vehicle and annotate the class with the @AutoClone annotation:
    import groovy.transform.AutoClone
    
    @AutoClone 
      class Vehicle {
      String brand
      String type
      Long wheelsNumber
    }
  2. In the same script where the Vehicle object is defined, add the following code:
    def v1 = new Vehicle()
    v1.brand = 'Ferrari'
    v1.type = 'Testarossa'
    v1.wheelsNumber = 4
    def v2 = v1.clone()
    
    assert v1 instanceof Cloneable
    assert v1.brand == v2.brand

    The script should execute without errors.

  3. Let's add a second object to the mix:
    class Engine {
      int horseEngine
      Number liter
    }
  4. Let's add the engine property to the Vehicle class:
    Engine engine
  5. Modify the test code, so that the vehicle is created with an engine:
    def v1 = new Vehicle(
               brand: 'Ferrari',
               type: 'Testarossa',
               wheelsNumber: 4
             )
    def e1 = new Engine(
               horseEngine: 390,
               liter: 4.9
             )
    v1.engine = e1 // assign engine to car
    def v2 = v1.clone() // clone
    
    println 'Original vehicle engine liters: ' +  v1.engine.liter
    println 'Cloned vehicle engine liters: ' + v2.engine.literv2.engine.liter = 8
    println 'Original vehicle engine liters: ' + v1.engine.liter
  6. The result of the modified script yields:
    Original vehicle engine liters: 4.9
    Cloned vehicle engine liters: 4.9
    Original vehicle engine liters: 8
    

How it works...

The snippet in step 2 shows how easy is to clone an object without having to implement any interface. The default cloning strategy implemented by the annotation will call super.clone before calling clone on each Cloneable property of the class. If a field or property is not cloneable, it is simply copied in a bitwise fashion. If some properties don't support cloning, a CloneNotSupportedException is thrown.

The decompiled Vehicle class has the following aspect (the code is cleaned up a bit for the sake of readability, as the Groovy-generated bytecode uses reflection heavily):

public class Vehicle implements Cloneable, GroovyObject {

  public Object clone() throws CloneNotSupportedException {
    Object result = super.clone()
    if (brand instanceof Cloneable) {
      result.brand = brand.clone()
    }
    if (type instanceof Cloneable) {
      result.type = type.clone()
    }
    if (wheelsNumber instanceof Cloneable) {
      result.wheelsNumber = wheelsNumber.clone()
    }
    return result
  }

}

In step 6, we can observe that the cloning process didn't quite work as expected. An attribute of the cloned object is modified, and the same attribute on the original object gets modified as well. Not surprisingly, the reason for this behavior is that the Engine class is not annotated with the @AutoClone annotation; therefore, the cloning process only clones the reference to the object, not the actual object. Simply annotate the Engine object with @AutoClone to have the actual object cloned.

There's more...

The @AutoClone annotation supports some additional cloning styles. The first cloning style is based on the Copy Constructor pattern:

import groovy.transform.*

@AutoClone(style=AutoCloneStyle.COPY_CONSTRUCTOR)
class Vehicle {
  ...
}

The clone method implementation is moved into the body of a constructor that takes a parameter of the same type as the class. Then, calls to clone simply return the result of calling this constructor. Enabling the COPY_CONSTRUCTOR pattern produces Java code similar to the following code snippet:

public class Vehicle implements Cloneable {

  private String brand;
  private String type;
  private Long wheelsNumber;

  protected Vehicle(Vehicle other) {
    brand = other.brand;
    type = other.type;
    wheelsNumber = other.wheelsNumber;
  }

  public Object clone() throws CloneNotSupportedException {
    return new Vehicle(this);
  }

}

If a class already implements Serializable, the @AutoClone annotation can be configured to use the Serialization style. This feature performs a deep copy automatically, attempting to copy the entire tree of objects including array and list elements. The AutoCloneStyle.SERIALIZATION style has some limitations; it is generally slower and it doesn't support fields marked with final.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset