我需要在Java中克隆一个子类,但是在发生这种情况的代码中,我不会知道子类类型,只知道超类。这样做的最佳设计模式是什么?
示例:
class Foo {
String myFoo;
public Foo(){}
public Foo(Foo old) {
this.myFoo = old.myFoo;
}
}
class Bar extends Foo {
String myBar;
public Bar(){}
public Bar(Bar old) {
super(old); // copies myFoo
this.myBar = old.myBar;
}
}
class Copier {
Foo foo;
public Foo makeCopy(Foo oldFoo) {
// this doesn't work if oldFoo is actually an
// instance of Bar, because myBar is not copied
Foo newFoo = new Foo(oldFoo);
return newFoo;
// unfortunately, I can't predict what oldFoo's the actual class
// is, so I can't go:
// if (oldFoo instanceof Bar) { // copy Bar here }
}
}
答案 0 :(得分:6)
如果您可以控制要复制的类,那么虚拟方法就是前进的方法:
class Foo {
...
public Foo copy() {
return new Foo(this);
}
}
class Bar extends Foo {
...
@Override public Bar copy() {
return new Bar(this);
}
}
(理想情况下,要使课程抽象或有效最终。)
答案 1 :(得分:3)
Java中最标准的方法是使每个可克隆的类实现Cloneable,并使用适用于该类的克隆的公共版本覆盖Object.clone()
(默认情况下{{ 1}}制作对象的浅表副本。)
请注意,许多人认为Object.clone()
/ Cloneable
是糟糕的设计。
答案 2 :(得分:2)
如果以正确方式实现clone / clonable,则不会出现任何超类问题。这是因为Object.clone()是一个非常特殊的方法 - 在一个非常简洁的描述中:Object.clone()将始终返回与其调用的类型相同的对象。
@see http://download.oracle.com/javase/6/docs/api/java/lang/Object.html#clone%28%29
因此克隆和克隆的正确实现将是:
class Foo implements Clonable{
String myFoo;
...
Foo clone() {
try {
Foo clone = (Foo) super.clone();
clone.myFoo = this.myFoo;
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException("this could never happen", e);
}
}
}
class Bar extends Foo {
String myBar;
...
Bar clone() {
try {
Bar clone = (Bar) super.clone();
clone.myBar = this.myBar();
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException("this could never happen", e);
}
}
}
然后复印机的实施很简单:
class Copier {
Foo foo;
/**
* @return return a Foo if oldFoo is of class Foo, return Bar if oldClass is of class Bar.
*/
public Foo makeCopy(Foo oldFoo) {
return oldFoo.clone();
}
}
答案 3 :(得分:0)
执行此操作的方法是在newInstance()
和Foo
中创建方法Bar
。这两个实现都可以创建一个正确类型的对象并返回它,复制代码只需要知道它必须使用oldFoo.newInstance()
来获取该对象的副本。
答案 4 :(得分:0)
如果需要深度克隆对象,最好的方法是使用Java序列化。这要求该对象实现Serializable,但它将创建一个全新的克隆对象,并且没有对原始对象的共享引用。
您可以让您的主类实现Serializable,因此每个子类都会自动支持它。
最后,这可能会更新为使用Piped Streams而不是ByteArrayOutputStream来使用更少的内存并且速度更快,但如果您有小对象则不会注意到它。
public static<T> T clone(T object) {
try {
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bOut);
out.writeObject(object);
out.close();
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray()));
T copy = (T)in.readObject();
in.close();
return copy;
}
catch(Exception e) {
throw new RuntimeException(e);
}
}
答案 5 :(得分:0)
我不确定这是否完全符合您的要求,但我建议您查看Factory pattern。
答案 6 :(得分:0)
此模式/解决方案使用复制构造函数和重写的类型化虚函数的组合,并将实例传播到每个超类。
public class A {
private String myA;
public A(A a) {
myA = a.myA;
}
public A copy() {
return new A(this);
}
}
public class B extends A {
private String myB;
public B(B b) {
super(b);
myB = b.myB;
}
public B copy() {
return new B(this);
}
}
public class C extends B {
private String myC;
public C(C c) {
super(c);
this.myC = c.myC;
}
public C copy() {
return new C(this);
}
}