clone()`clone()` は、 Java プログラミング言語におけるオブジェクト複製のためのメソッドです。Java では、オブジェクトは参照変数を通して操作され、オブジェクトをコピーする演算子は存在しません。代入演算子はオブジェクトではなく参照を複製します。`clone()` メソッドは、この不足している機能を提供します。
コピー機能を必要とするクラスは、そのためのメソッドを実装する必要があります。その機能は、ある程度「Object.clone()」によって提供されます。
clone()コピーコンストラクタのように動作します。通常は、clone()スーパークラスのメソッドを呼び出してコピーを取得し、最終的にObjectのメソッドに到達するまでこの処理を繰り返します。基底クラスのclone()特別なメソッドは、オブジェクトを複製するための標準的なメカニズムを提供します。clone()Object
クラスのメソッドは、同じクラスで、すべてのフィールドが 同じ値を持つオブジェクトのコピーを作成して返します。ただし、オブジェクトがマーカーインターフェースを実装するクラスのインスタンスでない限り、例外をスローします。Objectclone()Object.clone()CloneNotSupportedExceptionCloneable
のデフォルトの実装では、シャローコピーObject.clone()が実行されます。クラスがディープコピーやその他のカスタム動作を必要とする場合は、スーパークラスからコピーを取得した後、独自のメソッドでそれを実装する必要があります。clone()
cloneJavaで呼び出す構文は次のとおりです(objはパブリックメソッドを持つクラス型の変数であると仮定しますclone())。
オブジェクトのコピー= obj.clone ( ) ;または一般的に
MyClass copy = ( MyClass ) obj.clone ( ) ;これは、から返される一般的な参照をオブジェクトへの参照に割り当てるために必要な型変換を提供します。ObjectcloneMyClass
このメソッドの設計上の欠点の1つは、のclone()戻り値の型がであり、適切な型に明示的にキャストし直す必要があることです。しかし、をオーバーライドして適切な型を返すようにする方が望ましく、クライアント側でのキャストの必要性を排除できます( J2SE 5.0以降では共変戻り値型を使用します)。clone()Objectclone()
もう一つの欠点は、抽象型clone()に対してメソッドにアクセスできない場合が多いことです。Java のほとんどのインターフェースと抽象クラスは、パブリックメソッドを指定していません。そのため、多くの場合、メソッドはオブジェクトの実際のクラスがわかっている場合にのみ使用できますが、これは可能な限り汎用的な型を使用するという抽象化の原則に反します。たとえば、Java で参照を持っている場合、パブリック メソッドが指定されていないため、その参照に対してメソッドを呼び出すことはできません。やなどの実際の実装は一般的にメソッド自体を持っていますが、オブジェクトの実際のクラス型を持ち歩くのは不便で、抽象化としては良くありません。clone()clone()Listclone()Listclone()ListArrayListLinkedListclone()
には、特にコピーコンストラクタ(同じクラスの別のインスタンスをパラメータとして受け取るコンストラクタ)やファクトリメソッドclone()の使用といった代替手段があります。ただし、クローンするオブジェクトの具体的な型が事前にわからない場合は、これらの方法は必ずしも適切ではありません。(ただし、ほとんどの抽象クラスはパブリックメソッドを実装していないため、同じ理由で も適切でない場合が多いです。)clone()clone()
また、クローンを使用する代わりに、シリアライゼーションとデシリアライゼーションを使用することもできます。
シングルトンパターンを使用してクラスを作成する場合、そのクラスのインスタンスは一度に1つしか存在できません。そのため、クラスはクローンを作成することを許可してはなりません。これを防ぐには、clone()次のコードを使用してメソッドをオーバーライドします。
public Object clone () throws CloneNotSupportedException { throw new CloneNotSupportedException (); }これは、スーパークラスがパブリックclone()メソッドを実装している場合、またはサブクラスがこのクラスのclone()メソッドを使用してコピーを取得することを防ぐためにのみ必要です。クラスは通常、パブリックclone()メソッドを継承しません。なぜなら、Objectパブリックメソッドを持たないからclone()です。したがって、非関数型clone()メソッドを明示的に実装する必要は通常ありません。
任意の型のオブジェクトを適切にクローンできるようにするには、clone() メソッドが正しく宣言され、Object.clone() で説明されている規則に従って正しく実装されている必要があります。
1) クローンを作成する必要のあるすべての型は、自身のクラスにパブリックな clone() メソッドを持つか、親クラスのいずれかにパブリックにアクセス可能な clone() メソッドを持つ必要があります。
例:
型Yの変数varY1に対してclone()メソッドを呼び出すには、YまたはYの親クラスが公開アクセス可能なclone()メソッドを宣言する必要があります。ここでは、親クラスXが公開clone()メソッドを提供します。
public class X implements Cloneable { public X clone () throws CloneNotSupportedException { return ( X ) super . clone (); } }public class Y extends X { }public class Z extends Y { }public class test1 { public void function () throws CloneNotSupportedException { Y varY1 = new Z (); Y varY2 = ( Y ) varY1 . clone (); } }2) clone() メソッドを実装するすべてのクラスは、super.clone() を呼び出してクローンされたオブジェクト参照を取得する必要があります。クラスにクローンする必要のあるオブジェクト参照がある場合 (たとえば、ディープコピーの場合)、clone() メソッドはオブジェクトを返す前に必要な変更を行う必要があります。(Object.clone() は元のオブジェクトの正確なコピーを返すため、コレクションや配列などの可変フィールドは元のオブジェクトとコピーの間で共有されます。これはほとんどの場合、想定外であり、望ましくありません。)
例:
クラス Z はオブジェクト参照を含んでいるため、その clone() メソッドもそのオブジェクト参照を複製して、元のオブジェクトのディープコピーを返します。
public class X implements Cloneable { public X clone () throws CloneNotSupportedException { return ( X ) super . clone (); } }public class Y extends X { }public class ObjectABC implements Cloneable { public ObjectABC clone () throws CloneNotSupportedException { return ( ObjectABC ) super . clone (); } }public class Z extends Y { private ObjectABC someABC ;public Z clone () throws CloneNotSupportedException { Z newZ = ( Z ) super . clone (); newZ . someABC = someABC . clone ();return newZ ; } }public class test1 { public void function () throws CloneNotSupportedException { Y varY1 = new Z (); Y varY2 = ( Y ) varY1 . clone (); } }階層構造内のすべてのクラスがclone()メソッドを実装している場合、クローン作成時にこれらの関数がすべて呼び出されるため、オーバーヘッドが発生します。このオーバーヘッドは、繰り返し処理を繰り返すうちに、無視できないほど大きくなる可能性があります。
複雑なオブジェクトグラフの場合、再帰的な参照が存在すると、ディープコピーが問題になることもあります。
同じオブジェクトのコピーが複数存在する状態は、必ずしも適切とは限りません。特定の実装の目的がclone()利用者に十分に理解されていない場合、「単一オブジェクト、複数参照」というパラダイムを意図せず破ってしまう可能性があります。
一般的に、フィールドclone()とは互換性がありません。は基本的にデフォルトコンストラクタ(引数を持たないコンストラクタ)であるため、メソッド内でフィールドを割り当てることは不可能で、コンパイルエラーが発生します。フィールドの値が不変オブジェクトの場合は問題ありません。コンストラクタに参照をコピーさせれば、元のオブジェクトとクローンオブジェクトは同じオブジェクトを共有します。finalclone()finalclone()
しかし、値が可変オブジェクトである場合は、ディープコピーを行う必要があります。一つの解決策は、finalフィールドから修飾子を削除し、修飾子によってもたらされる利点を放棄することです。
このため、一部のプログラマーは、階層内のオブジェクトをシリアライズ可能にし、古いオブジェクトをシリアライズしてから、結果として得られるビットストリームから新しいオブジェクトを作成することでコピーを作成することを提案しています。この方法は最終的なデータメンバーを正しく処理しますが、処理速度は著しく低下します。[ 1 ]
あるいは、現在のオブジェクトのフィールドから完全に新しいオブジェクトを返すこともできます。これは、最初にコンストラクタを呼び出し、後で非finalフィールドを割り当てることで実現できます。別の代替方法は、このアイデアを形式化することです。つまり、インスタンスを受け取るコピーコンストラクタを作成します。実際、一部の人々はクローンよりもこの方法を推奨しています。 [ 2 ]