Edited at

Javaのcloneメソッドの正しい実装方法

More than 1 year has passed since last update.

cloneメソッドの実装を行うにはコツが必要なようなのでまとめてみたいと思います。


cloneメソッドとは

cloneメソッドとは、Javaの全クラスの親クラスであるObjectクラスが持っているメソッドの一つで、自分自身を複製するという役割を持っています。


cloneメソッドが必要な理由

自分自身を複製するのであれば、Hoge a=this;といった感じに記述すれば良いのではないか、と思われるかもしれません。しかし、Javaにおいて、クラス型の変数に入っているのは、インスタンスの中身への矢印(参照)なので、この場合、athisと全く同じインスタンスを指しているということになります。そのため、片方のフィールドを操作すると、もう片方のフィールドも書き換わってしまいます。このような事態を避けるためにcloneメソッドを利用する、というわけです。


Objectクラスのcloneメソッド


処理手順

Objectクラスのcloneメソッドの処理手順を大まかに示すと次のようになります。


  1. 複製するインスタンスのサイズを取得

  2. 新たなインスタンスの中身を生成

  3. 複製元のフィールドを丸ごとコピー

一見すると、これで完全な複製ができる、と思ってしまいますが、実はそうではありません。これは浅いコピーと呼ばれ、プリミティブ型の変数は複製されているものの、クラス型の変数は複製されていません。なぜなら、クラス型の変数の複製においては、インスタンスの中身への矢印(参照)を複製しているだけだからです。そのため、上で書いたのと同じことが起こってしまいます。


特徴

Objectクラスのcloneメソッドは次のような特徴を持っています。



  • protected修飾子が付いている


  • Clonableインターフェイスを実装していないインスタンスを複製しようとした場合、CloneNotSupportedExceptionが投げられてしまう

  • 返り値がObject型である

なお、Clonableインターフェイスは、マーカーインタフェースと呼ばれる、中身が空のインターフェイスで、Clonableインターフェイスの場合には、複製可能であるということを示しています。


cloneメソッドの正しい実装方法

以上のことから、cloneメソッドは次のようにオーバーライドするのが正しいということになります(Nodeクラスはcloneメソッドが正しくオーバーライドされているクラスとします)。


Example.java

public class Example implements Cloneable{ //Clonableインターフェイスを実装

private static Node u; //クラス型static変数(static変数は元々インスタンスの中身がひとつしかないので、浅いコピーで良い)
private Node s; //クラス型変数
private int p; //プリミティブ型変数

/*何らかのメソッドやコンストラクタが入る*/

@Override
public Example clone() { //基本的にはpublic修飾子を付け、自分自身の型を返り値とする
Example b=null;

/*ObjectクラスのcloneメソッドはCloneNotSupportedExceptionを投げる可能性があるので、try-catch文で記述(呼び出し元に投げても良い)*/
try {
b=(Example)super.clone(); //親クラスのcloneメソッドを呼び出す(親クラスの型で返ってくるので、自分自身の型でのキャストを忘れないようにする)
b.s=this.s.clone(); //親クラスのcloneメソッドで深いコピー(複製先のクラス型変数と複製元のクラス型変数で指しているインスタンスの中身が違うコピー)がなされていないクラス型変数をその変数のcloneメソッドで複製し、複製先のクラス型変数に代入
}catch (Exception e){
e.printStackTrace();
}
return b;
}
}



関連記事


参考文献