如何用Java中的最终字段克隆抽象对象?
在this问题和这post是解释如何通过使用受保护的拷贝构造函数克隆与最终字段的对象。如何用Java中的最终字段克隆抽象对象?
然而,假设我们有:
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);
}
}
hi @Eran:抽象类如何意识到它的子类的存在? –
@ΦXocę웃Пepeúpaツ它不知道它们,也不应该意识到它们。 – Eran
好吧,但......真是一种痛苦!这是唯一的方法吗?在这种情况下,我想我应该在'Person'中声明'public abstract Object clone()',对吧? – justHelloWorld
如果你只是想在类的新实例,而不克隆其成员的值,那么你可以使用以下命令:
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 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状态:
在某些情况下,如反序列化时,系统将需要改变施工后对象的
final
领域。final
字段可以通过反射和其他实现相关手段进行更改。其中有合理的语义的唯一模式是在其中构建对象,然后更新对象的字段。对象不应该对其他线程可见,也不应该读取对象字段,直到完成对对象的所有字段的更新。
这正是该示例的工作原理,在克隆构建后,在将克隆暴露给任何人之前设置final
字段,并且不读取任何字段。
如上所述,这是必需的情况,很少见。只要你可以实现一个基于拷贝构造函数的解决方案,就可以使用它。
您忘记提及此解决方案(以及基于反射ftm的任何反射)*无论何时重新命名受影响的字段都会自动断开,并且这将成为不早于运行时间的问题。 – hiergiltdiestfu
@hiergiltdiestfu:的确,尽管字段和clone方法驻留在同一个类中,并且审计工具应该能够通过静态代码分析来检查正确性。但通常情况下,使用Reflection意味着失去编译时检查... – Holger
当你想重写'clone'时,别忘了你可以使返回类型更具体('Person'而不是'Object'),并且它不需要声明'抛出CloneNotSupportedException'(你应该可以为'Brain'做这个)。 – maaartinus
我不禁停下来,认为你的问题实际上等于“我如何克隆一个人?”没有人认为这很奇怪,因为我们是程序员。 – Pharap
我认为这更多是一个设计问题。如果所有可能的Person类的实现都是未知的,我会问自己在这种情况下使用继承是否正确。 问题可以使用组合来解决吗? Person是一个可能的实现?因为如果你可以使用组合,解决方案很简单,它只是调用Person的构造函数并设置所有变量,否则,如果继承很重要,那么你知道在Person中需要一个'Person clone()'方法,所以声明它是抽象的,所有的实现将决定如何实现它。希望它有帮助,欢呼! – rascio