Java's Object Methods: clone()

Java's Object Methods: clone()

Introduction

This article is a continuation of a series of articles describing the often forgotten about methods of the Java language's base Object class. The following are the methods of the base Java Object which are present in all Java objects due to the implicit inheritance of Object.

The focus of this article is the clone() method which is used to generate distinctly separate copies (new instances) of an object. I should also note that the clone() method is probably one of the most controversial methods available on the Object class due to some odd behaviors and implementation features.

Why the Need to clone() an Object Exists

I would first like to start with why it may be necessary to create a clone or copy of an object in the first place. I will again be utilizing my Person class from prior articles in this series for demonstrations, of particular importance is that this is a mutable version of it, otherwise copying would be a moot point.

The code is show below:

import java.time.LocalDate;

public class Person {
    private String firstName;
    private String lastName;
    private LocalDate dob;

    public Person(String firstName, String lastName, LocalDate dob) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.dob = dob;
    }

    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }

    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }


    public LocalDate getDob() { return dob; }
    public void setDob(LocalDate dob) { this.dob = dob; }

    @Override
    public String toString() {
        return "<Person: firstName=" + firstName + ", lastName=" + lastName + ", dob=" + dob + ">";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((dob == null) ? 0 : dob.hashCode());
        result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
        result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Person)) {
            return false;
        }
        Person p = (Person)o;
        return firstName.equals(p.firstName)
                && lastName.equals(p.lastName)
                && dob.equals(p.dob);
    }
}

I begin my discussion by creating a couple of integer variables x and y along with an instance of Person and assign it to a variable named me. Then I assign me to another variable named me2 which afterwards I change the firstName field in me2 and show the contents of both variables, like so:

import java.time.LocalDate;

public class Main {
    public static void main(String[] args) {
        int x = 10;
        int y = x;
        y = 20;
        System.out.println("x = " + x);
        System.out.println("y = " + y);

        Person me = new Person("Adam", "McQuistan", LocalDate.parse("1987-09-23"));
        Person me2 = me;
        me2.setFirstName("Joe");
        System.out.println("me = " + me);
        System.out.println("me2 = " + me2);
    }
}

Output:

x = 10
y = 20
me = <Person: firstName=Joe, lastName=McQuistan, dob=1987-09-23>
me2 = <Person: firstName=Joe, lastName=McQuistan, dob=1987-09-23>

Now there is a good chance that many of you caught that little oops... but, just so everyone is on the same level of understanding let me explain what just happened there. In Java you have two main categories of data types: value types (aka, primitives) and reference types (aka, objects). In my example above the Person objects such as me and me2 are of reference type of Person object. In contrast to the Person reference types x and y are value types of int primitives.

As was just made apparent, assignment with reference types are treated differently then say an integer, or perhaps more accurately stated int in the Java language. When you assign a reference variable to another reference variable you are simply telling it the location where that object can be referenced in memory which is very different then the actual copying of contents that occurs when you do the same with value types.

This is why when I changed the value of the me2 reference variable's firstName field I also saw the same change in the me reference variable, they were referencing the same object in memory. For these reasons it becomes important to be able to create actual copies (clones) of reference objects and hence the need for the clone() method.

How to clone() an Object

As I alluded to earlier the clone() method of the Object class is a bit of a controversy in the Java programming community. The reasons for this is that in order to implement the clone() method you need to implement a quirky interface called Cloneable from the "java.lang" package which provides your class the ability to expose a public clone() method. This is necessary because the clone() method on the Object class is protected and thus not accessible from client code working with your class. Furthermore, the behavior of object creation is quite unusual in that the instance is created without invoking the coveted new operator which leaves many, myself included, a little uneasy.

However, for completeness I will be describing a valid way to implement a properly overridden clone() method while implementing the Cloneable interface but, I will also be ending with some alternative mechanisms for creating new instances of objects in a more idiomatic Java-esk manner.

Ok, without further banter I will proceed with explaining how to clone objects via clone() within my Person class. First I will implement the Cloneable interface and add the publicly overridden clone() method which returns an instance of type Object.

For a simple class like Person that does not contain any mutable fields all that is required is to create a clone is to return a call to the base class Object's clone method, like so:

public class Person implements Cloneable {
    private String firstName;
    private String lastName;
    private LocalDate dob;

    public Person(String firstName, String lastName, LocalDate dob) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.dob = dob;
    }

    // omitting other sections for brevity

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

In this example creating a Person clone is quite simple and accomplished like so:

public class Main {
    public static void main(String[] args) {
        Person me = new Person("Adam", "McQuistan", LocalDate.parse("1987-09-23"));
        Person me2 = null;
        try {
            me2 = (Person) me.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        me2.setFirstName("Joe");
        System.out.println("me = " + me);
        System.out.println("me2 = " + me2);
    }
}

Output:

me = <Person: firstName=Adam, lastName=McQuistan, dob=1987-09-23>
me2 = <Person: firstName=Joe, lastName=McQuistan, dob=1987-09-23>

An voilà a me clone is made. Now when I update the firstName property of me2 using the previous example the field is unchanged in the me object. Make sure to note the explicit cast of the returned clone of type Object to type Person which is necessary because the interface necessitates returning a reference of type Object.

Free eBook: Git Essentials

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

Unfortunately though this implementation of the clone() method will only work on simple value typed containing objects that do not have mutable reference properties. If I were to add a couple of mutable fields like mother of type Person and a family array of Person objects I would need to make a few changes to allow safe cloning to take place.

To demonstrate this I need to update my Person class like so.

public class Person implements Cloneable {
    private String firstName;
    private String lastName;
    private LocalDate dob;
    private Person mother;
    private Person[] family;

    public Person(String firstName, String lastName, LocalDate dob) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.dob = dob;
    }

    // omitting other methods for brevity

    public Person getMother() { return mother; }
    public void setMother(Person mother) { this.mother = mother; }

    public Person[] getFamily() { return family; }
    public void setFamily(Person[] family) { this.family = family; }

    @Override
    public Object clone() throws CloneNotSupportedException {
        Person personClone = (Person) super.clone();
        Person motherClone = (Person) mother.clone();
        Person[] familyClone = family.clone();
        personClone.setMother(motherClone);
        personClone.setFamily(familyClone);
        return personClone;
    }
}

In order to guarantee that the cloned object has it's own unique copies of the original object's mutable fields, mother and family, I must explicitly make copies of them via clone() or other ways like instantiating and setting the values via the new operator.

If I did not specifically take the time to individually make clones of these mutable fields then the two resultant Person objects would be referencing the same mother and family mutable object instances which would be a terrible mess to debug down the road. This explicit field by field copying of mutable object members is known as deep copying.

Alternate Techniques for Creating Instance Copies

There are a few other ways to create clones of objects that I have seen that use techniques such as serialization, copy constructors, and factory methods that create copies of objects. However, in this section I am only going to cover the latter two because I personally don't care much for using serialization for creating copies of objects.

To begin with I will cover the copy constructor method. This route of creating copies of objects using a constructor relies on a signature that contains just one parameter of its own type representing the object to be copied such as public Person(Person p).

Within the body of the copy constructor each field of the object to be copied is either directly assigned a new instance of that class in the case of value types or used to create new instances of their fields in the case of reference types.

Here is an example of using a copy constructor for the Person class:

public class Person implements Cloneable {
    private String firstName;
    private String lastName;
    private LocalDate dob;
    private Person mother;
    private Person[] family;

    public Person(String firstName, String lastName, LocalDate dob) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.dob = dob;
    }

    public Person(Person p) {
        this.firstName = new String(p.firstName);
        this.lastName = new String(p.lastName);
        this.dob = LocalDate.of(p.dob.getYear(),
                p.dob.getMonth(),
                p.dob.getDayOfMonth());
        if (p.mother != null) {
            this.mother = new Person(p.mother);
        }
        if (p.family != null) {
            this.family = new Person[p.family.length];
            for (int i = 0; i < p.family.length; i++) {
                if (p.family[i] != null) {
                    this.family[i] = new Person(p.family[i]);
                }
            }
        }
    }

    // omitting other methods for brevity

}

The other technique I will show uses a factory method. The factory method technique is essentially the same as a copy constructor except that the new copy is created inside a static factory method that returns a new instance as a copy, like so:

public class Person implements Cloneable {
    private String firstName;
    private String lastName;
    private LocalDate dob;
    private Person mother;
    private Person[] family;

    public Person(String firstName, String lastName, LocalDate dob) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.dob = dob;
    }

    public static Person makeCopy(Person p) {
        Person copy = new Person(new String(p.firstName),
                new String(p.lastName),
                LocalDate.of(p.dob.getYear(), p.dob.getMonth(), p.dob.getDayOfMonth()));
        if (p.mother != null) {
            copy.mother = Person.makeCopy(p.mother);
        }
        if (p.family != null) {
            copy.family = new Person[p.family.length];
            for (int i = 0; i < p.family.length; i++) {
                if (p.family[i] != null) {
                    copy.family[i] = Person.makeCopy(p.family[i]);
                }
            }
        }
        return copy;
    }

    // omitting other methods for brevity

}

Comparing the Implementation Differences

Creating copies of Java object via the route of implementing Cloneable and overriding clone() has rightly so garnered a bit of a bad reputation. This is due to the odd nature in witch the interface changes the visibility of the clone() method itself along with the often underemphasized need to "deep" clone mutable reference typed class fields. For these reasons I prefer to use copy constructors and factory methods to create copies of objects. It is only when I'm working with a class that has specifically implemented the Cloneable interface that I will proceed with using the clone() method.

Conclusion

In this article I have described the why's and how's of creating copies of objects in Java. I have covered the specifics the traditional but somewhat idiomatically strange way of copying through the implementation of the Cloneable interface in tandem with the clone() method as well as how to use copy constructors and static factory methods.

As always, thanks for reading and don't be shy about commenting or critiquing below.

Last Updated: March 22nd, 2019
Was this article helpful?

Improve your dev skills!

Get tutorials, guides, and dev jobs in your inbox.

No spam ever. Unsubscribe at any time. Read our Privacy Policy.

Adam McQuistanAuthor

I am both passionate and inquisitive about all things software. My background is mostly in Python, Java, and JavaScript in the areas of science but, have also worked on large ecommerce and ERP apps.

Make Clarity from Data - Quickly Learn Data Visualization with Python

Learn the landscape of Data Visualization tools in Python - work with Seaborn, Plotly, and Bokeh, and excel in Matplotlib!

From simple plot types to ridge plots, surface plots and spectrograms - understand your data and learn to draw conclusions from it.

© 2013-2024 Stack Abuse. All rights reserved.

AboutDisclosurePrivacyTerms