克隆是否比构造函数/工厂方法提供了性能改进?

发布于 2024-07-15 12:15:15 字数 526 浏览 4 评论 0原文

我正在维护一个较旧的 Java 代码库(jvm 1.4),它似乎使用克隆作为对象实例化的替代方法,我猜这是一种性能优化。 这是一个人为的例子:

public class Foo {
  private SomeObject obj; // SomeObject implements Cloneable
  public Foo() {
    obj = new SomeObject();
    obj.setField1("abc"); // these fields will have the same value every time
    obj.setField2("def");
  }
  public void doStuff() {
    SomeObject newObj = obj.clone(); // clone it instead of using a factory method
    // do stuff with newObj
  }
}

尽管有关于过早优化的常见警告,但这实际上是在某些时候推荐的习惯用法吗?

I'm maintaing an older Java code base (jvm 1.4) that seems to use cloning as an alternative to object instantiation, I'm guessing as a performance optimization. Here's a contrived example:

public class Foo {
  private SomeObject obj; // SomeObject implements Cloneable
  public Foo() {
    obj = new SomeObject();
    obj.setField1("abc"); // these fields will have the same value every time
    obj.setField2("def");
  }
  public void doStuff() {
    SomeObject newObj = obj.clone(); // clone it instead of using a factory method
    // do stuff with newObj
  }
}

The usual caveats about premature optimization notwithstanding, was this actually a recommended idiom at some point?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

临走之时 2024-07-22 12:15:16

想必他们想要一份副本。 也许他们想将其传递给另一个函数,并且不能确定该函数不会更改它。 这是确保方法 doStuff() 相对于其调用的 Foo 对象的状态而言是 const 的一种方法。

Presumably they wanted a copy. Perhaps they want to pass it to another function, and can't be sure that that function won't change it. It's a way of making sure that the method doStuff() is const with respect to the state of the Foo object it's called on.

一百个冬季 2024-07-22 12:15:16

调用clone()而不是复制构造函数或工厂方法的原因之一是其他选项可能都不可用。

与实现复制构造函数或工厂方法来执行相同操作相比,实现clone()来执行浅对象复制(更涉及深层复制)是微不足道的。 要实现 clone(),类只需实现 Cloneable 接口并使用调用 super 的方法重写 clone() 方法。 clone() 通常会调用 Object.clone()Object.clone() 将原始对象的每个属性复制到副本的相应属性中,从而创建浅表副本。

尽管实现clone()很简单,但仍然很容易忘记实现Cloneable。 因此,使用 clone() 复制对象的潜在风险是,如果该对象的类确实忽略实现 Cloneableclone() 直接或间接调用 Object.clone() ,都会抛出 CloneNotSupportedException

请参阅此代码示例以及之前关于讨论 href="https://stackoverflow.com/questions/1138769/why-is-the-clone-method-protected-in-java-lang-object/1138790#1138790">的设计不佳 >可克隆界面。

One reason to invoke clone() instead of the copy constructor or a factory method is that none of the other options may be available.

Implementing clone() to perform a shallow object copy (deep copy is more involved) is trivial compared to implementing a copy constructor or a factory method to perform the same operation. To implement clone(), a class need simply implement the Cloneable interface and override method clone() with one that invokes super.clone() which usually invokes Object.clone(). Object.clone() copies every property of the original object into the corresponding property of the duplicate, thus creating a shallow copy.

Though implementing clone() is straightforward, it is still easy to forget to implement Cloneable. Consequently, a potential risk of using clone() to duplicate an object is that if the class of that object does neglect to implement Cloneable and clone() invokes Object.clone() either directly or indirectly, it will throw CloneNotSupportedException.

See this code example and a previous discussion on the poor design of the Cloneable interface.

水溶 2024-07-22 12:15:16

复制构造函数的一个主要问题是必须在编译时知道对象的类型。 如果可继承类支持复制构造函数,并且构造函数传递派生类对象,则构造函数将生成一个基类对象,其基类属性通常与传入对象的基类属性匹配,但新对象不会不支持传入对象中存在但基类中不存在的任何功能。

通过将复制构造函数设置为“受保护”,并在每个派生类中使用可重写的工厂复制方法来调用该类自己的复制构造函数,进而调用其基类的复制构造函数,可以在一定程度上解决此问题。 不过,每个派生类都需要一个复制构造函数和复制方法的重写,无论它是否添加任何新字段。 如果案例类使用“clone”,则可以消除这个额外的代码。

One major problem with copy constructors is that the type of the object has to be known at compile time. If an inheritable class supports a copy constructor, and the constructor is passed a derived-class object, the constructor will produce a base-class object whose base-class properties generally match those of the passed-in object, but the new object won't support any features that were present in the passed-in object that weren't present in the base class.

It's possible to solve this problem somewhat by making a copy constructor "protected", and having an overridable factory copy method in every derived class which calls that class' own copy constructor, which in turn calls the copy constructor of its base class. Every derived class will need a copy constructor and an override of the copy method, though, whether or not it adds any new fields. If the case class uses "clone", this extra code can be eliminated.

缱倦旧时光 2024-07-22 12:15:16

这可能是一种性能优化,具体取决于构造函数中完成了多少工作。

它更可能被使用,因为语义不同。 克隆提供了一种用通常不倾向于这种方式的语言来实现“原型语义”(如 JavaScript、self 等)的方法。

It may be a performance optimization, depending on how much work is done in the constructors.

It's more likely used because the semantics are different. Cloning provides a way to implement "prototype semantics" (like in javascript, self, etc.) in a language that doesn't normally tend that way.

娇柔作态 2024-07-22 12:15:16

如果 SomeObject 构造函数执行昂贵的工作,例如从数据库中获取某些内容或解析某些内容,或从文件中读取某些内容,那么克隆将有意义以避免执行这些工作。

如果构造函数什么都不做,那么就真的没有必要使用克隆。

编辑:添加代码以表明克隆不必执行与构造函数相同的工作:

class Main
    implements Cloneable
{
    private final double pi;

    public Main()
    {
        System.out.println("in Main");
        // compute pi to 1,000,000,000 decimal palaces
        pi = 3.14f;
    }

    public Object clone()
    {
        try
        {
            return (super.clone());
        }
        catch(final CloneNotSupportedException ex)
        {
            throw new Error(); // would not throw this in real code
        }
    }


    public String toString()
    {
        return (Double.toString(pi));
    }

    public static void main(String[] args)
    {
        final Main a;
        final Main b;

        a = new Main();
        b = (Main)a.clone();

        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }
}

主构造函数被调用一次,计算 pi 被执行一次。

If the SomeObject constructor does expensive work, such as grabbing something from a database or parsing something, or reading something from a file then the clone would make sense to avoid doing the work.

If the constructor does nothing then there really is no need to use clone.

Edit: added code to show that clone does not have to do the same work as the constructor:

class Main
    implements Cloneable
{
    private final double pi;

    public Main()
    {
        System.out.println("in Main");
        // compute pi to 1,000,000,000 decimal palaces
        pi = 3.14f;
    }

    public Object clone()
    {
        try
        {
            return (super.clone());
        }
        catch(final CloneNotSupportedException ex)
        {
            throw new Error(); // would not throw this in real code
        }
    }


    public String toString()
    {
        return (Double.toString(pi));
    }

    public static void main(String[] args)
    {
        final Main a;
        final Main b;

        a = new Main();
        b = (Main)a.clone();

        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }
}

The Main construtor is called once, the computing pi is performed once.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文