在Java中实现Clone还有其他选择吗?

时间:2009-04-26 23:03:05

标签: java reference vector clone subclass

在我的Java项目中,我有一个各种类型的交易者的向量。这些不同类型的交易者是Trader类的子类。现在,我有一个方法,将Trader作为参数,并在向量中存储50次左右。我遇到了问题,因为存储相同的对象50次只是存储同一对象的50个引用。我需要存储50个副本的对象。我已经研究过如何实现Clone,但是我不希望定义一种Trader的程序员不必担心让他们的类可以克隆。另外,正如this page所指出的,实现clone会产生各种各样的问题。我认为复制构造函数不会起作用,因为如果我在Trader类中定义了一个,它就不会知道它正在复制的Trader的类型,只是制作一个通用的Trader。我该怎么办?

编辑:我真的不想制作某个特定对象的精确副本。我真正想要做的是在向量中添加一定数量的交易者。问题是用户需要在参数中指定他想要添加的交易者类型。这是我想要做的一个例子:(虽然我的语法完全是想象的)

public void addTraders(*traderType*)
{
    tradervect.add(new *traderType*())
}

如何在Java中实现这样的目标?

5 个答案:

答案 0 :(得分:2)

只需添加抽象复制方法即可。您可以使用协变返回类型,以便指定派生类型以返回派生实例,这可能重要也可能不重要。

public interface Trader {
    Trader copyTrader();
    ...
}


public final class MyTrader implements Trader {
    MyTrader copyTrader() {
        return new MyTrader(this);
    }
    ...
}

有时您可能希望一般性地处理需要克隆然后返回正确类型集合的派生类型Trader的集合。为此,您可以以惯用的方式使用泛型:

public interface Trader<THIS extends Trader> {
    THIS copyTrader();
    ...
}


public final class MyTrader implements Trader<MyTrader> {
    public MyTrader copyTrader() {
        return new MyTrader(this);
    }
    ...
}

答案 1 :(得分:2)

我有点不清楚你为什么要存储相同对象的50个左右的克隆,除非原始交易者作为后期交易者的原型(见模式)。

如果要制作对象的精确副本,则必须考虑多态性问题。如果允许对给定类进行子类化的人员添加状态成员,那么你已经对equals和compareTo等函数感到头疼,那么克隆就是另一种需要特殊处理的情况。

我不同意克隆总是邪恶的,有时是必要的。但是,在子类化的情况下,很多事情变得棘手。

我建议你阅读(当你有机会的时候)Bloch的“Effective Java”,它涵盖了很多他的主题。 Bracha的观点是,让别人扩展你的课程并不是一个好主意,如果你这样做,你需要很好地记录他们必须做的事情,并希望他们遵循你的指示。真的没有办法绕过那个。您可能还想让您的交易者不可变。

正常地执行一个实现Clone,并在类头中清楚地指出任何从Trader继承并添加状态成员的人必须实现X方法(指定哪个)。

寻找绕过实际Cloneable并仍然克隆的技巧不会克服继承问题。这里没有银弹。

答案 2 :(得分:0)

一个选项:如果您可以使对象可序列化,则可以序列化然后反序列化它以进行复制,类似于通过RMI传递对象时发生的情况。

快速复制方法:

public MyObject copy() {
    ObjectOutputStream oos = null;
    ObjectInputStream ois = null;
    try {
        ByteArrayOutputStream bos =  new ByteArrayOutputStream();
        oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        oos.flush();
        ByteArrayInputStream bin =
            new ByteArrayInputStream(bos.toByteArray());
        ois = new ObjectInputStream(bin);
        return (MyObject)ois.readObject();
    } catch(Exception e) {
        return null;
    } finally {
        try {
            oos.close();
            ois.close();
        } catch (Exception e) {
            return null;
        }
    }
}

答案 3 :(得分:0)

Uri是对的,对州的多态性开辟了一大堆蠕虫。

我认为继承Cloneable并覆盖clone()可能是最简单的方法。我相信你可以使返回类型协变。

答案 4 :(得分:-2)

一种方法是使它成为像Java自己的String这样的最终类,它将对类Trader的对象进行任何更改以在内存中创建新副本,但它将无法对其进行子类化。

另一种(更好的)方法是使用工厂方法来创建和复制Trader对象,这意味着您不能允许使用默认构造函数,即将其设为私有。这样您就可以控制类具有的实例数。 见http://en.wikipedia.org/wiki/Factory_method

public class Trader {

    /* prevent constructor so new cant be used outside the factory method */
    private Trader() {
    }

    /* the factory method */
    public static Trader createTrader(int whatKindOfTrader) {

        switch (whatKindOfTrader) {
        case 0:
            return new Trader1(); // extends Trader
        case 1:
        default:
            return new Trader2(); // extends Trader
        }
        return new Trader3(); // or throw exception
    }
}

您甚至可以指定另一个重载方法,或者第二个参数,它接受一个Trader并将其复制到一个新的,从而替换clone。 顺便说一句,你可能想要覆盖clone()方法并抛出CloneNotSupportedException,以防止默认的Object克隆。