2016-08-28 1 views
3

このクラスおよびそれを構成するフィールドの階層全体にclone()を実装することにより、特定のクラスのインスタンスをディープクローンします。これらのクラスに実装したclone()の実装では、元の対応するフィールド(this)にclone()を呼び出して、新しいインスタンスの各フィールドを割り当てます。それからメインクラスでclone()と呼んでいます。私はこれがディープクローニングの標準的な方法だと信じています。変更可能なオブジェクトへの共有参照を保持している間のディープクローン

以下に小さな実行可能な例があります。私が得たクローンは、実際のディープコピーです。各フィールドとサブフィールドに含まれるオブジェクトはすべて元のインスタンスのものと同じ新しいオブジェクトです。

しかし、元のフィールドabが同じオブジェクトXを参照していた場合、ディープクローンでは同じオブジェクト(Xのクローン)を参照しません。代わりに、それらはXの2つの異なるクローンを参照するでしょう。

オブジェクト全体を階層全体にディープクローンしてオブジェクトをディープクローンしたいのですが、階層に複数のフィールドで同じ参照が含まれている場合これらのフィールドの1つだけを新しいオブジェクトに深くクローンする必要があります。他のフィールドはこの新しいオブジェクトを参照するだけです。

簡単な解決策では問題に見えませんが、そのためのテクニックがあるのか​​、それを行うツールやライブラリがあるのか​​疑問です。

TestClone.java

public class TestClone { 

    public static void main(String[] args) throws CloneNotSupportedException { 

     // Create the object to share : 

     SharedObject shared = new SharedObject(1); 

     // Create the object to clone, which will own two Holder instances 
     // both holding a reference to *the same* object : 

     MainObject original = new MainObject(new Holder(shared), new Holder(shared)); 

     // Show that both holders hold a reference to the same object : 

     System.out.println("Original holder1 holds " + original.holder1.field.hashCode()); 
     System.out.println("Original holder2 holds " + original.holder2.field.hashCode()); 

     // Deep-clone the main object : 

     MainObject cloned = (MainObject) original.clone(); 

     // Show that the two cloned holders now hold a reference to *different* cloned objects : 

     System.err.println("Cloned holder1 holds " + cloned.holder1.field.hashCode()); 
     System.err.println("Cloned holder2 holds " + cloned.holder2.field.hashCode()); 

     // How to clone so that they will hold a reference to *the same* cloned object ? 
    } 

} 

SharedObject.java

public class SharedObject implements Cloneable { 

    public int n; 

    public SharedObject(int n) { 

     this.n = n; 
    } 

    @Override 
    protected Object clone() throws CloneNotSupportedException { 

     SharedObject clone = (SharedObject) super.clone(); 

     clone.n = this.n; 

     return clone; 
    } 

} 

Holder.java

public class Holder implements Cloneable { 

    public SharedObject field; 

    public Holder(SharedObject field) { 

     this.field = field; 
    } 

    @Override 
    protected Object clone() throws CloneNotSupportedException { 

     Holder clone = (Holder) super.clone(); 

     clone.field = (SharedObject) this.field.clone(); 

     return clone; 
    } 

} 

MainObject.java

public class MainObject implements Cloneable { 

    public Holder holder1; 

    public Holder holder2; 

    public MainObject(Holder holder1, Holder holder2) { 

     this.holder1 = holder1; 

     this.holder2 = holder2; 
    } 

    @Override 
    protected Object clone() throws CloneNotSupportedException { 

     MainObject clone = (MainObject) super.clone(); 

     clone.holder1 = (Holder) this.holder1.clone(); 

     clone.holder2 = (Holder) this.holder2.clone(); 

     return clone; 
    } 

} 

答えて

2

このようなクローン操作の「標準的な」方法はありません。さらに、私はそれをサポートするライブラリについて認識していません。

実際には、元のオブジェクトから複製されたオブジェクトに1対1のマッピング(バイジェクション)を組み込むことです。

マップ内にない場合は、各オブジェクトを複製し、ルートオブジェクトに対してクローンメソッドが呼び出されると、最初にそのような階層を構築する方法があります。その後、新しいクローン階層で参照をアセンブルします。

この手法は、すでにJavaのシリアライズ技術によって実装されています。すべてのクラスにSerializableを実装し、ObjectOutputStreampipe it to an ObjectInputStreamにルートオブジェクトを書き込み、すべてのオブジェクトを逆シリアル化します。シリアライズの仕組みは、あなたの要求を処理します。

+0

'SharedObject'、' Holder'と 'MainObject'がすべて' Serializable'を実装するようにテストコードを修正しました。 'clone()'実装をすべて削除し、 'TestClone'で' SerializationUtils.clone (メインオブジェクト) '私がそれを実行すると、クローン内の2つの「所有者」が同じ_cloned_ 'SharedObject'インスタンスへの参照を保持するようになりました。正確に私が必要としたもの。だから、この解決策で私は正しい方向に進んでいるようです。これを実際のコードに適用して、クローンするクラスはもちろんより大きく複雑になります。私はいくつかの問題に遭遇するかどうかを見ていきます。 – SantiBailors

+0

あなたを助けてうれしいです。 – Seelenvirtuose

+0

私はあなたのソリューションを実際のコードで実装しました。ありがたいことですが、他のテクニックと違って、これは深くクローンするだけでなく、共有リファレンスもクローンで共有されます。私はシリアライゼーションについて読んだことがあり、特に 'SerializationUtils.clone'はクローニングの選択肢でしたが、私は驚くべきことに他のクローニング技術とのこの重要な違いについては言及していませんでした。 – SantiBailors

1

短い答え:ことはできません。

長い答え:

深いクローニングとの事です:あなたはそれへのアクセスが良好を持っていません。結局のところ、クローン作成はJVM(多かれ少なかれ、黒い魔法)によって行われています。

これで、「標準的な」クローニング方法をどのように使うことができますか。何らかの "ルートオブジェクトX"をいくつかクローン作成するプロセスが何らかの "キャッシング"メカニズムを使用するようにしています。私は、JVMにそうするよう指示する既存のメカニズムについて注意しています。

短いストーリー:他の選択肢を検討しなければならないと思います。いくつかの合理的なフォーマットにシリアライズするように。その出力を処理します。

そして偉大answerからようやく引用する:

あなたはクローニング(コピーコンストラクタ、工場またはその同等を使用して対)は良いアイデアであると考えているように聞こえます。

そして、と続けた:今

は、もっと重要なのは、クローニングは悪い考えです。

+1

実際、クローニングは強力なテクニックであり、その有用性があります。おそらく高度な機能であり、正しく使用されなければ有害な可能性があります。 – Seelenvirtuose

+0

私の理解では、コピーコンストラクタは同じ問題を抱えています。私のコピーコンストラクタでは、私は各可変フィールドに 'new'インスタンスを割り当てます。したがって、クローン内の2つのフィールドは同じ参照を保持することはありませんが、オリジナルでは可能性があります。実際には、工場や他の技術でも、必要な方法でオリジナルを反映するオブジェクトが生成されることはありません。階層。私はシリアライゼーションがそれを言い表してくれてありがとう、私は試してみます。 – SantiBailors

関連する問題