如何在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();
…
}