Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to clone abstract objects with final fields in Java?

In this question and this post is explained how to clone objects with final fields by using protected copy constructors.

However, supposing that we have:

public abstract class Person implements Cloneable
{
    private final Brain brain; // brain is final since I do not want 
                // any transplant on it once created!
    private int age;
    public Person(Brain aBrain, int theAge)
    {
        brain = aBrain; 
        age = theAge;
    }
    protected Person(Person another)
    {
        Brain refBrain = null;
        try
        {
            refBrain = (Brain) another.brain.clone();
            // You can set the brain in the constructor
        }
        catch(CloneNotSupportedException e) {}
        brain = refBrain;
        age = another.age;
    }
    public String toString()
    {
        return "This is person with " + brain;
        // Not meant to sound rude as it reads!
    }
    public Object clone()
    {
        return new Person(this);
    }

    public abstract void Think(); //!!!!
    …
}

Returns an error since we can't instantiate an abstract class. How can we solve this?

like image 950
justHelloWorld Avatar asked Jun 14 '17 08:06

justHelloWorld


People also ask

Can we clone final object in Java?

clone() because final fields can only be changed through constructors. In our case, if we want every Person object to be unique by id, we will get the duplicate object if we use Object. clone() because Object. clone() will not call the constructor, and final id field can't be modified from Person.

Can we clone abstract class?

clone() will not work on interfaces and abstract classes. The only way to use the Object. clone() method is if the class of an object is known, i.e., we cannot access the clone() method on an abstract type since most interfaces and abstract classes in Java do not specify a public clone() method.

What is the return type of clone () method in the object class?

The class Object 's clone() method creates and returns a copy of the object, with the same class and with all the fields having the same values. However, Object. clone() throws a CloneNotSupportedException unless the object is an instance of a class that implements the marker interface Cloneable .


2 Answers

You don't implement the clone() method in the abstract class, only in the concrete sub-classes.

public class SomeConcretePerson extends Person
{
    public SomeConcretePerson (SomeConcretePerson another)
    {
        super (another); // this will invoke Person's copy constructor
    }

    public Object clone()
    {
        return new SomeConcretePerson(this);
    }
}
like image 88
Eran Avatar answered Oct 04 '22 18:10

Eran


In some rare cases, we might not be able to use the copy constructor technique and have to use the clone() method. For these cases, it’s worth knowing that Java offers a work-around for the final field problem:

public abstract class Person implements Cloneable {
    private final Brain brain;
    private int age;
    public Person(Brain aBrain, int theAge) {
        brain = aBrain; 
        age = theAge;
    }
    @Override public String toString() {
        return "This is person with " + brain;
        // Not meant to sound rude as it reads!
    }
    @Override public Person clone() {
        try {
            Person clone = (Person)super.clone();
            Field brainField=Person.class.getDeclaredField("brain");
            brainField.setAccessible(true);
            brainField.set(clone, brain.clone());
            return clone;
        } catch (CloneNotSupportedException|ReflectiveOperationException ex) {
            throw new AssertionError(ex);
        }
    }

    public abstract void think();

    …
}

The possibility to override the final restriction was created for exactly such use cases, cloning or deserializing an object, where no constructor will be called. The Java Language Specification, §17.5.3. Subsequent Modification of final Fields states:

In some cases, such as deserialization, the system will need to change the final fields of an object after construction. final fields can be changed via reflection and other implementation-dependent means. The only pattern in which this has reasonable semantics is one in which an object is constructed and then the final fields of the object are updated. The object should not be made visible to other threads, nor should the final fields be read, until all updates to the final fields of the object are complete.

This is exactly how the example works, setting the final field right after the clone’s construction, before the clone is exposed to anyone, and not reading any field.

As said, cases in which this is required, are rare. As long as you can implement a copy constructor based solution, use that.

like image 40
Holger Avatar answered Oct 04 '22 17:10

Holger