Joshua Bloch の『Effective Java』を読んでいました。そこで彼は、Clonable
インターフェースを使用しないことについて話しています。私は少し初心者なので、コードでクローン作成が必要になる場合のユースケースは何ですか? 概念を理解できるように、誰かが粘着性のある例を挙げてもらえますか?
4 に答える
インターフェイスは、オブジェクトのclone()
「浅い」コピーを作成するメカニズムを提供します。つまり、デフォルトでは、より多くのメモリがコピーに割り当てられ、オリジナルの各部分がコピーにコピーされます。対照的に、オブジェクト インスタンスを変数に単純に割り当てると、同じオブジェクトへの追加の参照が発生します。複製されたオブジェクト自体は真のコピーですが、それに含まれる要素はデフォルトでオリジナルから参照されたものを参照します。真の「ディープ」コピーが必要な場合は、clone()
メソッドを特殊化して、そのメンバーのクローンも作成する必要があります。
インターフェイスの使用例の 1 つclone()
は、オブジェクトのバージョン履歴を実装して、オブジェクトの古いバージョンにロールバックできるようにすることです。これは、データベースなどのトランザクション システムで使用できます。
もう 1 つの使用例は、コピー オン ライトの実装です。これは、オブジェクトのユーザーにオブジェクトの読み取り専用バージョンのみが提供される場合に役立ちます。
* newacct に感謝と称賛を込めて、クローンの説明を修正しました。
ウィキペディアのプロトタイプ パターンから、
...クローンしているオブジェクトの真のコピーである別のオブジェクトを実行時
clone()
に作成する場合は、オブジェクトが必要になります。真のコピーとは、新しく作成されたオブジェクトのすべての属性が、複製しているオブジェクトと同じであることを意味します。代わりにnewを使用してクラスをインスタンス化できた場合、すべての属性を初期値として持つオブジェクトを取得できます。たとえば、銀行口座取引を実行するためのシステムを設計している場合、口座情報を保持するオブジェクトのコピーを作成し、それに対して取引を実行してから、元のオブジェクトを変更したオブジェクトに置き換えます。このような場合、使用したいでしょうclone()
新しいのではなく。
Java でオブジェクトを複製するのには、十分な理由があります。次のコードを検討してください。
MyObj first = new MyObj(someOwner, someTag, someInt);
MyObj second = first;
この場合、そのオブジェクトの参照 (メモリ アドレス) をコピーしているだけです。変数firstとsecondは、どちらも MyObj クラスの同じインスタンスを参照します。clone() メソッドが達成することになっているのは、いわゆるディープ コピーです。もちろん、これは実装に依存します。クローン メソッドの開発者は、ディープ コピーが実際に達成されていることを確認する必要があります。したがって、コード:
MyObj third = (MyObj) first.clone();
この場合 clone() が行うことは、すべてのfirstのインスタンス メンバーを調べてそれらをコピー/クローン化し、それらの値を使用してまったく新しい MyObj インスタンスを作成することです。次に、新しいインスタンスへの参照を返すため、3 番目は同じインスタンスへの参照ではなく、1 番目のコピーです。
コメントでの質問への回答として、クローンがメンバー変数の新しいクローンを作成するか、単に参照をコピーするかは、実装によって異なります。クラス Person と NameTag も存在すると仮定して、MyObj サンプル クラスの次の実装を検討してください。MyObj オブジェクトのクローンを作成する場合、新しいクローンが元のオブジェクトと同じ Owner インスタンスを参照するようにしたい場合がありますが、NameTag のディープ コピーを作成します (もちろん、これは架空のクラスを使用した例にすぎません)。これは、MyObj インスタンスと NameTag インスタンス間の 1 対 1 の関係、および Owner と MyObj インスタンス間の1対多の関係を表します。次のコードは、質問で言及されている両方のケースを考慮しています。
class MyObj implements Cloneable {
private Person owner;
private NameTag tag;
private int size;
public MyObj(Person owner, NameTag tag, int size) {
this.owner = owner;
this.tag = tag;
this.size = size;
}
public Object clone() {
MyObj result;
//call all super clone() methods before doing class specific work
//this ensures that no matter where you are in the inheritance heirarchy,
//a deep copy is made at each level.
try {
result = (MyObj) super.clone();
}
catch (CloneNotSupportedException e) {
throw new RuntimeException
("Super class doesn't implement cloneable");
}
//work specific to the MyObj class
result.owner = owner; //We want the reference to the Person, not a clone
result.tag = tag.clone() //We want a deep copy of the NameTag
result.size = size; //ints are primitive, so this is a copy operation
return result;
}
一例として、パラメータの防御的コピー (有効な Java 項目 39: 必要に応じて防御的コピーを作成する) があります。
class X {
private byte[] a;
X(byte[] a) {
this.a = a.clone();
}
...