clone
メソッドの実装を行うにはコツが必要なようなのでまとめてみたいと思います。
cloneメソッドとは
clone
メソッドとは、Javaの全クラスの親クラスであるObject
クラスが持っているメソッドの一つで、自分自身を複製するという役割を持っています。
cloneメソッドが必要な理由
自分自身を複製するのであれば、Hoge a=this;
といった感じに記述すれば良いのではないか、と思われるかもしれません。しかし、Javaにおいて、クラス型の変数に入っているのは、インスタンスの中身への矢印(参照)なので、この場合、a
はthis
と全く同じインスタンスを指しているということになります。そのため、片方のフィールドを操作すると、もう片方のフィールドも書き換わってしまいます。このような事態を避けるためにclone
メソッドを利用する、というわけです。
Objectクラスのcloneメソッド
処理手順
Object
クラスのclone
メソッドの処理手順を大まかに示すと次のようになります。
- 複製するインスタンスのサイズを取得
- 新たなインスタンスの中身を生成
- 複製元のフィールドを丸ごとコピー
一見すると、これで完全な複製ができる、と思ってしまいますが、実はそうではありません。これは浅いコピーと呼ばれ、プリミティブ型の変数は複製されているものの、クラス型の変数は複製されていません。なぜなら、クラス型の変数の複製においては、インスタンスの中身への矢印(参照)を複製しているだけだからです。そのため、上で書いたのと同じことが起こってしまいます。
特徴
Object
クラスのclone
メソッドは次のような特徴を持っています。
-
protected
修飾子が付いている -
Clonable
インターフェイスを実装していないインスタンスを複製しようとした場合、CloneNotSupportedException
が投げられてしまう - 返り値が
Object
型である
なお、Clonable
インターフェイスは、マーカーインタフェースと呼ばれる、中身が空のインターフェイスで、Clonable
インターフェイスの場合には、複製可能であるということを示しています。
cloneメソッドの正しい実装方法
以上のことから、clone
メソッドは次のようにオーバーライドするのが正しいということになります(Node
クラスはclone
メソッドが正しくオーバーライドされているクラスとします)。
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;
}
}