2009-03-19 10 views
7

私はオブジェクトのインスタンス化の代替としてクローンを使用しているような古い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 
    } 
} 

にもかかわらず、時期尚早の最適化に関する通常の注意点は、これはいくつかの点で実際に推奨イディオムでしたか?

+0

奇妙に思えます。私は新しいオブジェクトのインスタンス化のためにコンストラクタを使用します。 –

答えて

3

コピーコンストラクタまたはファクトリメソッドの代わりにclone()を呼び出す理由の1つは、他のオプションが利用できないことです。

clone()を実装すると、浅いオブジェクトコピー(深いコピーがより関与します)を実行するのは、コピーコンストラクタまたはファクトリメソッドを実装して同じ操作を実行するのと比べて簡単です。 clone()を実装するには、クラスは単にCloneableインターフェイスを実装し、メソッドclone()をオーバーライドして、Object.clone()を呼び出すを呼び出すメソッドをオーバーライドする必要があります。 Object.clone()は、元のオブジェクトのすべてのプロパティを複製の対応するプロパティにコピーして、浅いコピーを作成します。

clone()の実装は簡単ですが、Cloneableの実装を忘れても簡単です。その結果、clone()を使用してオブジェクトを複製する潜在的なリスクは、そのオブジェクトのクラスがCloneableおよびclone()の実装を無視してObject.clone()を直接的または間接的に呼び出す場合、CloneNotSupportedExceptionがスローされることです。

code exampleとインターフェイスのpoor designの前のdiscussionを参照してください。

4

おそらく彼らはコピーを望んでいました。おそらく彼らはそれを別の関数に渡したいと思うかもしれませんし、その関数がそれを変更しないことを確かめることはできません。これは、メソッドdoStuff()が呼び出されたFooオブジェクトの状態に関してconstであることを確認する方法です。

+0

これを上げて+1すると(コードを見た後)、私のより難解な答えよりも適用される可能性が高いと思われます。 – MarkusQ

+0

これはクローニングの主な目的であることに注意してください(私の教授が彼らの話を知っていると仮定して)。 – Chris

+0

+1:うん、ここにあるようだ.clone()は、値渡しというJavaの方法です。 –

1

コンストラクタでの作業量に応じて、パフォーマンスの最適化が行われる可能性があります。

セマンティクスが異なるために使用される可能性が高くなります。クローニングは、通常はそうしない言語で "prototype semantics"(javascript、selfなど)を実装する方法を提供します。

+0

どうしてですか?プロトタイプのセマンティクスでは、実行時にコンストラクタやその他のフィールドやメソッドの動作を変更することができただけです。 –

+0

クローニングでは、初期値などを設定し、(デリゲートを使用して)動作を変更することができます。それは一種のクルージュですが、一般的にはフルスタイルのセルフスタイルのセマンティクスではなく、ジュースのほんの一部であるため、実際にはよく機能します。 – MarkusQ

0

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は一度行われると呼ばれています。

+0

あなたがそれについて不正確であると感じるものについて、少なくともコメントして何かをマークしようとするならば! – TofuBeer

+0

コピーコンストラクタが高価な作業をした場合、クローンは同じ作業を行う必要があります。 –

+0

クローンがコンストラクタをバイパスします。どうすれば同じことをする必要がありますか? – TofuBeer

2

コピーコンストラクタの大きな問題の1つは、コンパイル時にオブジェクトの型を知る必要があることです。継承可能クラスがコピーコンストラクタをサポートしており、コンストラクタが派生クラスオブジェクトを渡された場合、コンストラクタはベースクラスオブジェクトを生成します。そのベースクラスプロパティは、渡されたオブジェクトのプロパティと概ね一致しますが、渡されたオブジェクトに存在し、基底クラスに存在しなかったフィーチャーをサポートしていません。

コピーコンストラクタを "protected"にし、そのクラスの独自のコピーコンストラクタを呼び出すすべての派生クラスにオーバーライド可能なファクトリコピーメソッドを持たせることで、この問題を幾分解決できますクラス。ただし、新しいフィールドを追加するかどうかにかかわらず、すべての派生クラスにコピーコンストラクタとコピーメソッドのオーバーライドが必要です。ケースクラスが "クローン"を使用する場合、この余分なコードは削除することができます。

関連する問題