如何在 Java 中克隆具有 final 字段的抽象对象?

How to clone abstract objects with final fields in Java?

this question and this post 中解释了如何使用受保护的复制构造函数克隆具有最终字段的对象。

但是,假设我们有:

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 一个错误,因为我们无法实例化抽象 class。我们如何解决这个问题?

您没有在抽象 class 中实现 clone() 方法,仅在具体的子 class 中实现。

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);
    }
}

如果您只想要 class 的新实例而不克隆其成员的值,那么您可以使用以下方法:

public  static < T > T getNewInstance ( Class <T> type )
{
    try 
    {
        return type.newInstance()  ;
    } catch ( InstantiationException | IllegalAccessException e) 
    {
        e.printStackTrace();
    }
    return null ;
}

对于对象的深度克隆,您可以使用 com.rits.cloning.Cloner 实用程序。例如。 :

private T clone(T resource){

    Cloner cloner = new Cloner();
    T cloneObject = (T) cloner.deepClone(obj);
    return cloneObject;
}

我们不能实例化一个抽象class但是我们可以在子类中做到

class Teacher extends Person {

    public Teacher(Brain aBrain, int theAge) {
        super(aBrain, theAge);
    }

    protected Teacher(Person another) {
        super(another);
    }


    public Object clone() {
        return new Teacher(this);
    }
}

在极少数情况下,我们可能无法使用复制构造函数技术而不得不使用clone()方法。对于这些情况,值得一提的是 Java 提供了 final 字段问题的解决方法:

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();

    …
}

覆盖 final 限制的可能性正是为此类用例创建的,克隆或反序列化对象,其中不会调用构造函数。 Java Language Specification, §17.5.3. Subsequent Modification of final Fields 状态:

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.

这正是示例的工作原理,在克隆构建之后立即设置 final 字段,在克隆暴露给任何人之前,并且不读取任何字段。

如前所述,需要这样做的情况很少见。只要您可以实现基于复制构造函数的解决方案,就使用它。