如何在Java中克隆带有最终字段的抽象对象?

如何在Java中克隆带有最终字段的抽象对象?,java,abstract-class,clone,Java,Abstract Class,Clone,这将解释如何使用受保护的复制构造函数克隆具有最终字段的对象 然而,假设我们有: 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 Pe

这将解释如何使用受保护的复制构造函数克隆具有最终字段的对象

然而,假设我们有:

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(); //!!!!
    …
}

返回错误,因为无法实例化抽象类。我们如何解决这个问题?

您不在抽象类中实现
clone()
方法,只在具体的子类中实现

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

如果只需要类的新实例而不克隆其成员的值,则可以使用以下方法:

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

我们不能实例化抽象类,但可以在子类中实例化

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
限制的可能性正是为这样的用例创建的,即克隆或反序列化对象,在这些用例中不会调用构造函数。各国:

在某些情况下,例如反序列化,系统将需要在构建后更改对象的
final
字段<代码>最终字段可以通过反射和其他依赖于实现的方式进行更改。唯一具有合理语义的模式是构造对象,然后更新对象的
final
字段。在对象的
final
字段的所有更新完成之前,不应使对象对其他线程可见,也不应读取
final
字段

这正是示例的工作方式,在克隆构建完成后,克隆暴露给任何人之前,设置
final
字段,而不读取任何字段


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

hi@Eran:抽象类是如何意识到其子类的存在的??@ΦXocę웃Пepeúpaツ 它没有意识到它们,也不应该意识到它们。好吧,但是……多痛苦啊!这是唯一的办法吗?在这种情况下,我想我应该在
Person
中声明
公共抽象对象clone()
,对吗?@justHelloWorld您当然可以,但您不必这样做。@ohgodspider:实现
Cloneable
有什么意义?这里根本不使用它。当您费心重写
clone
时,不要忘记您可以使返回类型更具体(
Person
而不是
Object
),并且它不需要声明
抛出CloneNotSupportedException
(您可能应该为
Brain
这样做).我忍不住停下来,认为你的问题实际上相当于“我如何克隆一个人?”我们中没有人认为这很奇怪,因为我们是程序员。我认为这更像是一个设计问题。如果Person类的所有可能实现都还不清楚,我会问自己在这种情况下使用继承是否正确。这个问题能用构图法解决吗?哪一种是Person的可能实现?因为如果您可以使用组合,解决方案很简单,只需调用Person的构造函数并设置所有变量,否则,如果继承很重要,您知道您需要一个
Person clone()
方法,所以将其声明为抽象,所有实现将决定如何实现它。希望有帮助,干杯!您忘了提到,每当重命名受影响的字段时,此解决方案(以及任何基于反射ftm的反射)都会自动中断,这将在运行时之前成为一个问题。@hiergiltdiestfu:的确,尽管字段和克隆方法位于同一个类中,审计工具应该能够通过静态代码分析检查正确性。但一般来说,使用反射意味着丢失编译时检查…
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();

    …
}