Javaについて説明しているチュートリアルを探してCloneable
いましたが、適切なリンクが得られず、スタックオーバーフローがいずれにせよより明確な選択肢になりつつあります。
以下について知りたいのですが。
Cloneable
Cloneable
インターフェースを実装することで、オブジェクトのクローンまたはコピーを持つことができることを意味します。それを行うことの利点と欠点は何ですか?- オブジェクトが複合オブジェクトの場合、再帰的な複製はどのように行われますか?
Javaについて説明しているチュートリアルを探してCloneable
いましたが、適切なリンクが得られず、スタックオーバーフローがいずれにせよより明確な選択肢になりつつあります。
以下について知りたいのですが。
Cloneable
Cloneable
インターフェースを実装することで、オブジェクトのクローンまたはコピーを持つことができることを意味します。それを行うことの利点と欠点は何ですか? 回答:
最初に知っておくべきことCloneable
は、使用しないことです。
クローンをCloneable
正しく実装することは非常に難しく、その努力はそれに見合うものではありません。
その代わりに、apache-commons SerializationUtils
(deep-clone)やBeanUtils
(shallow-clone)などの他のオプションを使用するか、単にコピーコンストラクターを使用します。
を使用したクローン作成に関するJosh Blochのビューについては、ここを参照してくださいCloneable
。これは、このアプローチの多くの欠点を説明しています。(Joshua BlochはSunの従業員であり、数多くのJava機能の開発を率いていました。)
static
、インターフェースにメソッドを含めることができるので、static WhatEverTheInterface copy(WhatEverTheInterface initial)
?しかし、クローン中にオブジェクトからフィールドをコピーするので、これが何をもたらすのだろうと思いますが、インターフェイスはメソッドのみを定義します。説明する気?
Cloneable自体は、残念ながら単なるマーカーインターフェイスです。つまり、clone()メソッドを定義していません。
何が行われるかは、Cloneableを実装しないクラスに対してCloneNotSupportedExceptionをスローし、実装するクラスに対してメンバーごとの浅いコピーを実行する、保護されたObject.clone()メソッドの動作を変更することです。
これがあなたが探している振る舞いである場合でも、それを公開するために独自のclone()メソッドを実装する必要があります。
独自のclone()を実装する場合、考えられるのは、super.clone()によって作成されたオブジェクトから開始することです。これは、正しいクラスであることが保証されており、浅いコピーが何でもない場合に備えて、フィールドの追加を行います。あなたが欲しい。clone()からコンストラクターを呼び出すと、サブクラスが独自の追加の複製可能なロジックを追加する場合に継承が壊れるため、問題が発生します。この場合、super.clone()を呼び出すと、間違ったクラスのオブジェクトが取得されます。
このアプローチは、コンストラクターで定義される可能性のあるロジックをすべてバイパスします。
もう1つの問題は、clone()のオーバーライドを忘れたサブクラスは、デフォルトの浅いコピーを自動的に継承することです。これは、可変状態(ソースとコピーの間で共有される)の場合には望みのものとはおそらく異なります。
ほとんどの開発者はこれらの理由でCloneableを使用せず、代わりに単にコピーコンストラクターを実装します。
Cloneableの詳細と潜在的な落とし穴については、Joshua Bloch著「Effective Java」を強くお勧めします。
したがって、Cloneableは慎重に使用してください。それはあなたがすべてを正しく行うために適用する必要がある努力と比較してあなたに十分な利点を与えません。
クローニングは基本的なプログラミングパラダイムです。Javaが多くの点で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を実装していません。それが気に入らない場合は、別の名前を付けてください。
Cloneableの欠点は何ですか?
コピーしているオブジェクトが構成を持っている場合、クローンは非常に危険です。クローンは浅いコピーを作成するため、この場合に考えられる以下の副作用について考慮する必要があります。
db関連の操作を処理するオブジェクトが1つあるとします。たとえば、そのオブジェクトにはConnection
、プロパティの1つとしてオブジェクトがあります。
したがって、誰かがのクローンをoriginalObject
作成すると、作成されているオブジェクトは、としましょうcloneObject
。ここoriginalObject
とcloneObject
で同じ参照を保持Connection
オブジェクトを。
オブジェクトをoriginalObject
閉じたとしましょう。オブジェクトがオブジェクト間で共有され、実際にによって閉じられたため、は機能しなくなります。Connection
cloneObject
connection
originalObject
プロパティとしてIOStreamを持つオブジェクトを複製したい場合も同様の問題が発生する可能性があります。
オブジェクトが複合オブジェクトの場合、再帰的な複製はどのように行われますか?
Cloneableは浅いコピーを実行します。つまり、元のオブジェクトとクローンオブジェクトのデータは同じ参照/メモリを指します。反対に、ディープコピーの場合、元のオブジェクトのメモリからデータがクローンオブジェクトのメモリにコピーされます。
Cloneable
コピーは行いませんObject.clone
。「元のオブジェクトのメモリからのデータは、クローンオブジェクトのメモリにコピーされます」というのは、まさに何をするかObject.clone
です。深いコピーを説明するには、参照オブジェクトのメモリについて話す必要があります。