クローニングは、コンストラクタ/ファクトリメソッドよりもパフォーマンスの向上を提供していますか?
-
20-08-2019 - |
質問
私はインスタンス化オブジェクトの代替としてクローニングを使用しているようだ(JVM 1.4)古いJavaコードベースをmaintaingよ、私はパフォーマンスの最適化と推測しています。ここでは不自然な例だ。
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
}
}
時期尚早の最適化に関する通常の警告にもかかわらず、だったいくつかの点で、この実際に推奨イディオム?
解決
の代わりにコピーコンストラクタまたはファクトリメソッドのclone()
起動する一つの理由は、他のオプションのいずれも利用可能でないかもしれないということである。
浅いオブジェクトコピーを実行するCloneable
実装(ディープコピーはより複雑である)同じ操作を実行するコピーコンストラクタまたはファクトリメソッドを実装に比べ自明です。 super.clone()
実装するには、クラスは単に通常Object.clone()
を呼び出したCloneNotSupportedException
を呼び出す1と<=>インタフェースとオーバーライドメソッドを実装する必要が<=>。 <=>従って浅いコピーを作成する重複の対応するプロパティにコピー元のオブジェクトのすべてのプロパティを、
、<=>実装することを忘れて、まだ簡単です。その結果、オブジェクトを複製する<=>使用しての潜在的なリスクは、そのオブジェクトのクラスが実装するのに無視しなければということです<=>と<=>呼び出す<=>直接または間接的に、それは、<=>スローされます。
こののコード例を参照してください>と、前のディスカッションの上の貧しい設計する <=>インタフェースます。
他のヒント
おそらく彼らはコピーを望んでいました。おそらく彼らは別の関数に渡したい、そしてその機能はそれを変更しないことを確認することはできません。それは確かに方法がdoStuff()はそれが上と呼ばれていますのFooオブジェクトの状態に関してのconstであることを作る方法です。
コピーコンストラクタと1つの主要な問題は、オブジェクトの型がコンパイル時に知らなければならないことです。継承クラスはコピーコンストラクタをサポートし、コンストラクタが派生クラスのオブジェクトを渡された場合、コンストラクタは、その基本クラスのプロパティ、一般的に、渡されたオブジェクトのものと一致する基底クラスのオブジェクトを生成しますが、新しいオブジェクトが」勝ちましたTは基本クラスには存在しなかった渡されたオブジェクトに存在した任意の機能をサポートしています。
これは、「保護された」コピーコンストラクタを作成し、今度はその基底クラスのコピーコンストラクタを呼び出し、そのクラス自身のコピーコンストラクタを呼び出して、すべての派生クラスでオーバーライド工場のコピー方法を、持っていることによって多少この問題を解決することが可能です。すべての派生クラスは、それがどんな新しいフィールドを追加するかどうか、しかし、コピーコンストラクタとコピーメソッドのオーバーライドが必要になります。ケースクラスは「クローン」を使用している場合は、この余分なコードをなくすことができます。
これは、コンストラクタで行われているどのくらいの作業に応じて、パフォーマンスを最適化することがあります。
セマンティクスが異なるため、これは、使用済みの可能性が高いです。クローニングは、通常、そのように傾向がない言語では(ジャバスクリプト、自己、などのように)「プロトタイプのセマンティクス」を実装するための方法を提供します。
その後、クローンは、作業を行うことを避けるために理にかなってます。
コンストラクタは何もしない場合は、そして本当にクローンを使用する必要はありません。
編集:コンストラクタと同じ作業を行う必要はありません。そのクローンを表示するためにコードを追加しました:
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が一度実行され、一度呼び出されます。