私は次のことを知りたいです:
Cloneable
これは、インターフェイスを実装することで、オブジェクトのクローンまたはコピーを作成できることを意味しCloneable
ます。それを行うことの長所と短所は何ですか?- オブジェクトが複合オブジェクトの場合、再帰的なクローン作成はどのように行われますか?
最初に知っておくべきことCloneable
は、使用しないでください。
正しいクローンを実装することは非常に困難Cloneable
であり、努力する価値はありません。
その代わりに、apache-commons SerializationUtils
(deep-clone)やBeanUtils
(shallow-clone)などの他のオプションを使用するか、単にコピーコンストラクターを使用します。
によるクローン作成に関するJoshBlochの見解については、ここを参照してくださいCloneable
。これは、このアプローチの多くの欠点を説明しています。(Joshua BlochはSunの従業員であり、多数のJava機能の開発を主導しました。)
残念ながら、Cloneable自体は単なるマーカーインターフェイスです。つまり、clone()メソッドを定義していません。
つまり、保護されたObject.clone()メソッドの動作を変更します。これにより、Cloneableを実装していないクラスに対してCloneNotSupportedExceptionがスローされ、実装しているクラスに対してメンバーごとの浅いコピーが実行されます。
これがあなたが探している振る舞いであるとしても、それを公開するためにあなた自身のclone()メソッドを実装する必要があります。
独自のclone()を実装する場合、アイデアは、super.clone()によって作成されたオブジェクトから始めます。これは、正しいクラスであることが保証されています。次に、浅いコピーが適切でない場合に備えて、フィールドの追加の入力を行います。あなたが欲しい。clone()からコンストラクターを呼び出すと、サブクラスが独自のクローン可能なロジックを追加したい場合に継承が壊れるため、問題が発生します。この場合、super.clone()を呼び出すと、間違ったクラスのオブジェクトが取得されます。
このアプローチは、コンストラクターで定義されている可能性のあるロジックをバイパスしますが、これは潜在的に問題になる可能性があります。
もう1つの問題は、clone()のオーバーライドを忘れたサブクラスは、デフォルトの浅いコピーを自動的に継承することです。これは、可変状態(ソースとコピーの間で共有されるようになります)の場合に必要なものではない可能性があります。
ほとんどの開発者は、これらの理由でCloneableを使用せず、代わりにコピーコンストラクターを実装するだけです。
Cloneableの詳細と潜在的な落とし穴については、JoshuaBlochの著書EffectiveJavaを強くお勧めします。
したがって、Cloneableは慎重に使用してください。すべてを正しく行うために適用する必要がある努力と比較して、それはあなたに十分な利益を与えません。
クローニングは基本的なプログラミングパラダイムです。Javaが多くの点で不十分に実装している可能性があるという事実は、クローン作成の必要性をまったく減らさない。また、浅い、深い、混合など、どのように機能させても機能するクローン作成を簡単に実装できます。関数にcloneという名前を使用して、必要に応じてCloneableを実装しないこともできます。
クラス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実装に問題がある理由を示しています。しかし、この方法で簡単に克服できます。
クラス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つあるとします。たとえば、そのオブジェクトConnection
にはプロパティの1つとしてオブジェクトがあります。
したがって、誰かがのクローンを作成するoriginalObject
と、作成中のオブジェクトは、たとえば、になりcloneObject
ます。ここで、originalObject
とはオブジェクトcloneObject
の同じ参照を保持しConnection
ます。
オブジェクトをoriginalObject
閉じるとしましょう。オブジェクトはオブジェクト間で共有されており、によって実際に閉じられているため、は機能しません。Connection
cloneObject
connection
originalObject
IOStreamをプロパティとして持つオブジェクトのクローンを作成したい場合にも、同様の問題が発生する可能性があります。
オブジェクトが複合オブジェクトの場合、再帰的なクローン作成はどのように行われますか?
Cloneableは浅いコピーを実行します。つまり、元のオブジェクトとクローンオブジェクトのデータは同じ参照/メモリを指します。逆に、ディープコピーの場合、元のオブジェクトのメモリからのデータがクローンオブジェクトのメモリにコピーされます。