私はこのようにコード化された防御的なコピーを見てきました
void someMethod(Date d) {
myDate = new Date( d.getTime() );
}
しかし、それは私には意味がありません。Javaで、そのオブジェクトのメモリに同一のコピーを作成する方法はありませんか?
すべての場合に機能するとは限らないことを読みましたclone()
が、その理由はわかりません。
私はこのようにコード化された防御的なコピーを見てきました
void someMethod(Date d) {
myDate = new Date( d.getTime() );
}
しかし、それは私には意味がありません。Javaで、そのオブジェクトのメモリに同一のコピーを作成する方法はありませんか?
すべての場合に機能するとは限らないことを読みましたclone()
が、その理由はわかりません。
これに答えようとすることもできますが、Josh Bloch を盗用しているだけなので、代わりにリソースへのリンクを次に示します。
コピー コンストラクターとクローン作成
Bill Venners: あなたの著書では、実装
Cloneable
して記述する代わりにコピー コンストラクターを使用することを推奨していますclone
。それについて詳しく説明していただけますか?Josh Bloch:私の本でクローンに関する項目を読んだことがあるなら、特に行間を読んだことがあれば、クローンが深く壊れていると私が考えていることがわかるでしょう。いくつかの設計上の欠陥がありますが、その最大のものは、
Cloneable
インターフェイスにメソッドがないことclone
です。そして、それは単にうまくいかないことを意味します。何かを作ることは、それでCloneable
何ができるかについて何も言いません. 代わりに、内部で何ができるかについて何かを述べています。super.clone
繰り返し呼び出してObject
のメソッドを呼び出すことになった場合clone
、このメソッドは元のフィールドのコピーを返す、ということです。しかし、インターフェイスを実装するオブジェクトで何ができるかについては何も述べていません。つまり、多態的な操作
Cloneable
を行うことはできません。clone
の配列がある場合Cloneable
、その配列を実行し、すべての要素を複製して配列のディープ コピーを作成できると思うかもしれませんが、それはできません。には publicメソッドがなく、 にもないため、何かをキャストしてメソッドCloneable
を呼び出すことはできません。メソッドにキャストして呼び出そうとすると、コンパイラは、オブジェクトの保護されたメソッドを呼び出そうとしていると言います。clone
Cloneable
clone
Object
Cloneable
clone
clone
問題の真実は、コピー機能以外の
Cloneable
パブリックメソッドを実装および提供することによって、クライアントに機能を提供していないということです。clone
これは、別の名前でコピー操作を提供し、実装しない場合に得られるものよりも優れていますCloneable
。これは基本的に、コピー コンストラクターで行っていることです。コピー コンストラクターのアプローチにはいくつかの利点があり、それについては本で説明しています。大きな利点の 1 つは、オリジナルとは異なる表現を持つようにコピーを作成できることです。たとえば、 を にコピーできLinkedList
ますArrayList
。
Object
のclone
方法は非常にトリッキーです。これはフィールド コピーに基づいており、「言語外」です。コンストラクターを呼び出さずにオブジェクトを作成します。コンストラクターによって確立された不変条件が保持されるという保証はありません。super.clone
オブジェクトのクローンを作成するまでチェーンを繰り返し呼び出すと、オブジェクトの浅いコピーが作成されるという事実に起因して、Sun の内外で長年にわたって多くのバグが発生しています。クローンは通常、クローンされるオブジェクトと状態を共有します。その状態が変更可能な場合、2 つの独立したオブジェクトはありません。一方を変更すると、他方も変更されます。そして突然、ランダムな動作になります。もう使っているものはほとんどあり
Cloneable
ません。clone
人々がそれを期待しているので、私はよく具象クラスに public メソッドを提供します。抽象クラスを実装したり、インターフェイスを拡張したりしません。これは、抽象クラス (またはインターフェイス) を拡張 (または実装) するすべてのクラスCloneable
に実装の負担をかけないためです。Cloneable
それは本当に負担であり、メリットはほとんどありません。Doug Lea はさらに先へ進みます。
clone
彼は、配列をコピーする以外にはもう使っていないと私に言いました。を使用clone
して配列をコピーする必要があります。これが一般的に最速の方法だからです。しかし、Doug の型はCloneable
もはや実装されていません。彼はそれをあきらめました。そして、それは不合理ではないと思います。
Cloneable
壊れているのは残念ですが、起こります。オリジナルの Java API は、市場のクロージング ウィンドウに間に合うように、厳しい締め切りの中で非常に迅速に作成されました。オリジナルの Java チームは素晴らしい仕事をしましたが、すべての API が完璧というわけではありません。Cloneable
は弱点であり、人々はその限界を認識する必要があると思います。
clone()
JVMまたはコンパイラが作成しない複製に関連する多くの決定があるため(たとえば、浅いコピーと深いコピー)、一部のクラスでのみ意味のある実装が行われます。また、Java の概念全体が壊れているため、めったに使用されないと主張する人もいます。
ただし、最終的には、多くのクラスを複製できません。このような場合、ユーザーは、1 つのオブジェクトを別のオブジェクトに基づいて生成および初期化することにより、それらのクローンを作成します。
ただし、Date
クラスは を実装Cloneable
しているため、「コピー コンストラクター」を使用する場合と比較して、この特定のケースでは実際にはメリットがありません。
複製が望ましい状況の 1 つは、クラス階層を操作している場合です。最も外側の式を参照する Expression サブタイプのツリーとして数式を表現していると想像してください。
この場合はプラスだとしましょう。Expression への参照で clone を呼び出しますが、実際に得られるのは Plus の新しいインスタンスです。Expression はインターフェイスまたは抽象クラスになる可能性があるため、コンストラクターでは実際にはそれを行うことはできません。
常に機能する同一のコピーを作成する簡単な方法はありません。
クローンはそれを行うことになっていましたが、すべてのクラスで実装されているわけではないため、多かれ少なかれ壊れています。
別のアプローチは、シリアル化/逆シリアル化することですが、シリアル化もすべてのクラスでサポートされているわけではありません。
モバイルコードのセキュリティの観点から、clone
通常はオーバーライドできます。
@Override public Date clone() {
return this; // Ha!
}
セキュリティを気にしない場合でも、プログラマーが「賢く」コードにバグレポートを作成していることを想像できます。
clone
特にまったく同じランタイムタイプを返すという期待も、実装上の問題を引き起こします。