DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Newsletter
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • From Database Changes to Real-Time Actions at Scale: Oracle GoldenGate and Java
  • Java 27 Makes G1 the Default Everywhere: How to Test Your Small Containers Before You Upgrade
  • Stop Writing Database Migration Scripts: Declarative Schema Sync for Java
  • Searching Kafka Without an Index: Lessons From Building Message Search for Kafdrop

Trending

  • Building a Windows App for a Resilient SSH SOCKS Proxy and Selective Browser Routing
  • How to Evaluate Coding Agents With Harbor Framework, Python, Docker
  • Member Spotlight: Shai Almong
  • The Pipeline Failed, But the Infrastructure Changed

Draft This article hasn't been published, so readers can't see it.

In moderation This article is waiting for review, so readers can't see it yet.

Deleted This article has been deleted, so readers can't see it.

  1. DZone
  2. Coding
  3. Java
  4. Java Cloning: Even Copy Constructors Are Not Enough

Java Cloning: Even Copy Constructors Are Not Enough

Copy constructors are helpful, but they aren't polymorphic. A no-argument method can help bridge the gap to keep your code loosely coupled and easy to clone.

By 
Naresh Joshi
·
Updated Aug. 19, 19 · Tutorial
Post on X (opens in a new tab) Share on LinkedIn (opens in a new tab)
30.4K Views

Join the DZone community Save articles, join the discussion, and download Refcards and Trend Reports with a free account.

Join for free Already a member? Log in

This is the third article in my Java Cloning series. In my previous articles, Java Cloning and Types of Cloning (Shallow and Deep) in Details with Example and Java Cloning: Copy Constructor versus Cloning, I discussed Java cloning in detail and explained every concept, like what cloning is, how it works, the necessary steps we need to follow to implement cloning, how to use Object.clone(), shallow and deep cloning, how to achieve cloning using serialization and copy constructors, and advantages copy of copy constructors over Java cloning.Why-Copy-Constructors-Are-Not-Sufficient

If you have read those articles, you can easily understand why it is good to use copy constructors over cloning or Object.clone(). In this article, I am going to discuss why copy constructors are not sufficient.

Yes, you are reading that right—copy constructors are not sufficient by themselves. Copy constructors are not polymorphic because constructors do not get inherited by the child class from the parent class. If we try to refer a child object from a parent class reference, we will face problems cloning it using the copy constructor. To understand it, let’s take examples of two classes—Mammal and Human—where Human extends Mammal— and the Mammal class has one field type and two constructors, one to create objects and one copy constructor to create a copy of an object:

class Mammal {

    protected String type;

    public Mammal(String type) {
        this.type = type;
    }

    public Mammal(Mammal original) {
        this.type = original.type;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Mammal mammal = (Mammal) o;

        if (!type.equals(mammal.type)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return type.hashCode();
    }

    @Override
    public String toString() {
        return "Mammal{" + "type='" + type + "'}";
    }
}


And the Human class, which extends Mammal class, has one name field, one normal constructor, and one copy constructor to create copies:

class Human extends Mammal {

    protected String name;

    public Human(String type, String name) {
        super(type);
        this.name = name;
    }

    public Human(Human original) {
        super(original.type);
        this.name = original.name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        if (!super.equals(o)) return false;

        Human human = (Human) o;

        if (!type.equals(human.type)) return false;
        if (!name.equals(human.name)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = super.hashCode();
        result = 31 * result + name.hashCode();
        return result;
    }

    @Override
    public String toString() {
        return "Human{" + "type='" + type + "', name='" + name + "'}";
    }
}


Now let’s create objects for both classes:

Mammal mammal = new Mammal("Human");
Human human = new Human("Human", "Naresh");


Now if we want to create a clone for mammal or human, we can simply do it by calling their respective copy constructor:

Mammal clonedMammal = new Mammal(mammal);
Human clonedHuman = new Human(human);


We will get no error in doing this and both objects will be cloned successfully, as we can see in the tests below:

System.out.println(mammal == clonedMammal); // false
System.out.println(mammal.equals(clonedMammal)); // true

System.out.println(human == clonedHuman); // false
System.out.println(human.equals(clonedHuman)); // true


But what if we try to refer an object of Human from a reference of Mammal?

Mammal mammalHuman = new Human("Human", "Mahesh");


In order to clone mammalHuman, we cannot use the constructor Human. It will give us a compilation error because the type mammalHuman is of type Mammal, whereas the constructor of the Human class expects Human.

Mammal clonedMammalHuman = new Human(mammalHuman); // compilation error


And if we try clone mammalHuman using the copy constructor of Mammal, we will get a Mammal object instead of Human, but mammalHuman holds the object of Human:

Mammal clonedMammalHuman = new Mammal(mammalHuman);


So both mammalHuman and clonedMammalHuman are not the same objects as you see in the output code:

System.out.println("Object " + mammalHuman + " and copied object " + clonedMammalHuman + " are == : " + (mammalHuman == clonedMammalHuman));
System.out.println("Object " + mammalHuman + " and copied object " + clonedMammalHuman + " are equal : " + (mammalHuman.equals(clonedMammalHuman)) + "\n");


Output:

Object Human{type='Human', name='Mahesh'} and copied object Mammal{type='Human'} are == : false
Object Human{type='Human', name='Mahesh'} and copied object Mammal{type='Human'} are equal : false


As we can see, copy constructors suffer from inheritance problems and are not polymorphic. So how can we solve this problem? Well, there are various solutions, like creating static factory methods or creating some generic class that will do this for us—the list goes on.

But there is a very easy solution that will require copy constructors and will be polymorphic as well. We can solve this problem using defensive copy methods—a method we are going to include in our classes and call copy constructors from.

Defensive copy methods will also give us the advantage of dependency injection. We can inject dependencies instead of making our code tightly coupled. We can make it loosely coupled, we can even create an interface that will define our defensive copy method, and then we can implement it in our class and override that method.

So in the Mammal class, we will create a no-argument method cloneObject. However, we are free to name this method anything, like clone or copy or copyInstance

public Mammal cloneObject() {
    return new Mammal(this);
}


And we can override the same in the Human class:

@Override
public Human cloneObject() {
    return new Human(this);
}


Now to clone mammalHuman, we can simply say:

Mammal clonedMammalHuman = mammalHuman.cloneObject();


And for last two sys outs, we will get the below output, which is our expected behavior.

Object Human{type='Human', name='Mahesh'} and copied object Human{type='Human', name='Mahesh'} are == : false
Object Human{type='Human', name='Mahesh'} and copied object Human{type='Human', name='Mahesh'} are equal : true


As we can see, apart from getting the advantage of polymorphism, this option also gives us freedom for passing any argument. You can find the complete code in my CopyConstructorExample Java file on GitHub, and please feel free to give your valuable feedback.

Cloning Java (programming language)

Published at DZone with permission of Naresh Joshi. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • From Database Changes to Real-Time Actions at Scale: Oracle GoldenGate and Java
  • Java 27 Makes G1 the Default Everywhere: How to Test Your Small Containers Before You Upgrade
  • Stop Writing Database Migration Scripts: Declarative Schema Sync for Java
  • Searching Kafka Without an Index: Lessons From Building Message Search for Kafdrop

Partner Resources

×

Comments

Loading comments

The comments didn't load

Please try again in a moment.

Likes

Loading

The next likes didn't load. Please try again.

The likes didn't load

Please try again in a moment.

See who liked this

Log in or join DZone for free to see who liked this article.

Log in Join for free
  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook