102

私は次のことを知りたいです:

  1. Cloneableこれは、インターフェイスを実装することで、オブジェクトのクローンまたはコピーを作成できることを意味しCloneableます。それを行うことの長所と短所は何ですか?
  2. オブジェクトが複合オブジェクトの場合、再帰的なクローン作成はどのように行われますか?
4

6 に答える 6

166

最初に知っておくべきことCloneableは、使用しないでください。

正しいクローンを実装することは非常に困難Cloneableであり、努力する価値はありません。

その代わりに、apache-commons SerializationUtils(deep-clone)やBeanUtils(shallow-clone)などの他のオプションを使用するか、単にコピーコンストラクターを使用します。

によるクローン作成に関するJoshBlochの見解については、ここを参照してくださいCloneable。これは、このアプローチの多くの欠点を説明しています。(Joshua BlochはSunの従業員であり、多数のJava機能の開発を主導しました。)

于 2010-11-02T20:40:09.890 に答える
41

残念ながら、Cloneable自体は単なるマーカーインターフェイスです。つまり、clone()メソッドを定義していません。

つまり、保護されたObject.clone()メソッドの動作を変更します。これにより、Cloneableを実装していないクラスに対してCloneNotSupportedExceptionがスローされ、実装しているクラスに対してメンバーごとの浅いコピーが実行されます。

これがあなたが探している振る舞いであるとしても、それを公開するためにあなた自身のclone()メソッドを実装する必要があります。

独自のclone()を実装する場合、アイデアは、super.clone()によって作成されたオブジェクトから始めます。これは、正しいクラスであることが保証されています。次に、浅いコピーが適切でない場合に備えて、フィールドの追加の入力を行います。あなたが欲しい。clone()からコンストラクターを呼び出すと、サブクラスが独自のクローン可能なロジックを追加したい場合に継承が壊れるため、問題が発生します。この場合、super.clone()を呼び出すと、間違ったクラスのオブジェクトが取得されます。

このアプローチは、コンストラクターで定義されている可能性のあるロジックをバイパスしますが、これは潜在的に問題になる可能性があります。

もう1つの問題は、clone()のオーバーライドを忘れたサブクラスは、デフォルトの浅いコピーを自動的に継承することです。これは、可変状態(ソースとコピーの間で共有されるようになります)の場合に必要なものではない可能性があります。

ほとんどの開発者は、これらの理由でCloneableを使用せず、代わりにコピーコンストラクターを実装するだけです。

Cloneableの詳細と潜在的な落とし穴については、JoshuaBlochの著書EffectiveJavaを強くお勧めします。

于 2010-11-02T20:50:55.377 に答える
13
  1. クローン作成は、コンストラクターを使用せずに、オブジェクトを構築するための言語外の方法を呼び出します。
  2. クローンを作成するには、CloneNotSupportedExceptionを使用して何らかの方法で処理するか、それを処理するためにクライアントコードを煩わせる必要があります。
  3. 利点はわずかです。コピーコンストラクターを手動で作成する必要はありません。

したがって、Cloneableは慎重に使用してください。すべてを正しく行うために適用する必要がある努力と比較して、それはあなたに十分な利益を与えません。

于 2010-11-02T20:45:26.607 に答える
8

クローニングは基本的なプログラミングパラダイムです。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を実装していません。それが気に入らない場合は、別の名前を付けてください。

于 2014-02-15T22:13:48.490 に答える
5

A)コピーコンストラクターに比べてクローンの利点はそれほど多くありません。おそらく最大のものは、まったく同じ動的型の新しいオブジェクトを作成する機能です(宣言された型がクローン可能であり、パブリッククローンメソッドを持っていると仮定します)。

B)デフォルトのクローンはシャローコピーを作成し、クローン実装がそれを変更しない限り、シャローコピーのままになります。特にクラスに最終フィールドがある場合、これは難しい場合があります

Bozhoは正しいです、クローンは正しくするのが難しいかもしれません。コピーコンストラクタ/ファクトリはほとんどのニーズに対応します。

于 2010-11-02T20:48:56.040 に答える
0

Cloneableの欠点は何ですか?

コピーしているオブジェクトに合成がある場合、クローン作成は非常に危険です。この場合、クローンは浅いコピーを作成するため、考えられる副作用を以下で考慮する必要があります。

db関連の操作を処理するオブジェクトが1つあるとします。たとえば、そのオブジェクトConnectionにはプロパティの1つとしてオブジェクトがあります。

したがって、誰かがのクローンを作成するoriginalObjectと、作成中のオブジェクトは、たとえば、になりcloneObjectます。ここで、originalObjectとはオブジェクトcloneObjectの同じ参照を保持しConnectionます。

オブジェクトをoriginalObject閉じるとしましょう。オブジェクトはオブジェクト間で共有されており、によって実際に閉じられているため、は機能しません。ConnectioncloneObjectconnectionoriginalObject

IOStreamをプロパティとして持つオブジェクトのクローンを作成したい場合にも、同様の問題が発生する可能性があります。

オブジェクトが複合オブジェクトの場合、再帰的なクローン作成はどのように行われますか?

Cloneableは浅いコピーを実行します。つまり、元のオブジェクトとクローンオブジェクトのデータは同じ参照/メモリを指します。逆に、ディープコピーの場合、元のオブジェクトのメモリからのデータがクローンオブジェクトのメモリにコピーされます。

于 2016-03-13T05:43:23.293 に答える