私はJava Cloneable
について説明するチュートリアルを探していましたが、良いリンクを取得できませんでした。スタックオーバーフローはとにかく明白な選択肢になりました。
次のことを知りたいです。
Cloneable
は、Cloneable
インターフェイスを実装することで、オブジェクトのクローンまたはコピーを作成できることを意味します。それを行うことの利点と欠点は何ですか?Cloneable
について最初に知っておくべきことは-使用しないでください。
Cloneable
権限でクローンを実装することは非常に難しく、努力する価値はありません。
その代わりに、Apache-commons SerializationUtils
(deep-clone)または BeanUtils
(shallow-clone)などの他のオプションを使用します。または、単純にコピーコンストラクターを使用します。
こちらをご覧くださいCloneable
を使用したクローン作成に関するJosh Blochの見解については、このアプローチの多くの欠点を説明しています。 ( Joshua Bloch はSunの従業員であり、多数のJava機能を開発しました。)
残念ながら、Cloneable自体は単なるマーカーインターフェイスです。つまり、clone()メソッドを定義しません。
実行されるのは、保護されたObject.clone()メソッドの動作を変更することです。このメソッドは、Cloneableを実装しないクラスに対してCloneNotSupportedExceptionをスローし、実行するクラスに対してメンバーごとの浅いコピーを実行します。
これが探している動作であっても、それを公開するためには独自のclone()メソッドを実装する必要があります。
独自のclone()を実装するときの考え方は、正しいクラスであることが保証されているsuper.clone()によって作成されたオブジェクトから開始し、浅いコピーがそうでない場合にフィールドの追加の追加を行うことですあなたが欲しい。 clone()からコンストラクターを呼び出すと、サブクラスが独自のクローン可能なロジックを追加したい場合に継承が壊れてしまうため、問題が生じます。 super.clone()を呼び出す場合、この場合、間違ったクラスのオブジェクトを取得します。
ただし、このアプローチは、コンストラクターで定義されている可能性のあるロジックをバイパスします。
もう1つの問題は、clone()のオーバーライドを忘れたサブクラスが自動的にデフォルトのシャローコピーを継承することです。これは、変更可能な状態(ソースとコピー間で共有される)の場合に必要なものではない可能性があります。
ほとんどの開発者はこれらの理由からCloneableを使用せず、代わりに単純にコピーコンストラクターを実装します。
Cloneableの詳細と潜在的な落とし穴については、Joshua Bloch著のEffective Java
したがって、Cloneableを慎重に使用してください。すべてを正しく行うために適用する必要がある努力と比較して、十分な利点が得られません。
クローニングは基本的なプログラミングパラダイムです。 Javaが多くの点でそれを不十分に実装しているかもしれないという事実は、クローン作成の必要性を少しも減らさない。 、深い、混合、何でも。あなたが望むならCloneableを実装しないで、関数に名前cloneを使うことさえできます。
クラスA、B、Cがあり、BとCはAから派生しているとします。次のようなタイプAのオブジェクトのリストがある場合:
ArrayList<A> list1;
現在、そのリストにはタイプA、B、またはCのオブジェクトを含めることができます。オブジェクトのタイプがわからない場合。したがって、次のようにリストをコピーすることはできません。
ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
list2.add(new A(a));
}
オブジェクトが実際にタイプBまたはCである場合、正しいコピーを取得できません。また、Aが抽象的である場合はどうなりますか?現在、一部の人々はこれを提案しています:
ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
if(a instanceof A) {
list2.add(new A(a));
} else if(a instanceof B) {
list2.add(new B(a));
} else if(a instanceof C) {
list2.add(new C(a));
}
}
これは非常に悪い考えです。新しい派生型を追加するとどうなりますか? BまたはCが別のパッケージにあり、このクラスでそれらにアクセスできない場合はどうなりますか?
あなたがしたいことはこれです:
ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
list2.add(a.clone());
}
多くの人が、基本的なJava cloneの実装に問題がある理由を示しています。しかし、この方法で簡単に克服できます。
クラスAの場合:
public A clone() {
return new A(this);
}
クラスBで:
@Override
public B clone() {
return new B(this);
}
クラスCで:
@Override
public C clone() {
return new C(this):
}
同じ関数名を使用するだけで、Cloneableを実装していません。気に入らない場合は、別の名前を付けてください。
A)コピーコンストラクターに対するクローンの利点はそれほど多くありません。おそらく最大のものは、まったく同じ動的型の新しいオブジェクトを作成する機能です(宣言された型がクローン可能であり、パブリッククローンメソッドがあると仮定します)。
B)デフォルトのクローンは浅いコピーを作成し、クローンの実装で変更されない限り、浅いコピーのままになります。特にクラスに最終フィールドがある場合、これは難しい場合があります
Bozhoは正しい、クローンを正しく取得するのは難しい場合があります。コピーコンストラクタ/ファクトリは、ほとんどのニーズに対応します。
Cloneableの短所は何ですか?
コピーするオブジェクトに構成がある場合、クローン作成は非常に危険です。この場合、クローンはシャローコピーを作成するため、以下の考えられる副作用を考慮する必要があります。
Db関連の操作を処理するオブジェクトが1つあるとします。そのオブジェクトのプロパティの1つとしてConnection
オブジェクトがあるとします。
だから誰かがoriginalObject
のクローンを作成すると、作成されるオブジェクト、たとえばcloneObject
ができます。ここで、originalObject
およびcloneObject
は、Connection
オブジェクトの同じ参照を保持します。
originalObject
がConnection
オブジェクトを閉じるとしましょう。したがって、cloneObject
オブジェクトはそれらの間で共有され、実際にはconnection
によって閉じられたため、originalObject
は機能しません。
IOStreamをプロパティとして持つオブジェクトを複製したい場合、同様の問題が発生する可能性があります。
オブジェクトが複合オブジェクトである場合、再帰的クローンはどのように発生しますか?
Cloneableはシャローコピーを実行します。つまり、元のオブジェクトとクローンオブジェクトのデータは同じ参照/メモリを指します。逆に、ディープコピーの場合、元のオブジェクトのメモリのデータはクローンオブジェクトのメモリにコピーされます。