Javaでオブジェクトをコピーする方法:cloneメソッドの使い方

java

参照型変数(主に、自分で作成したクラスのオブジェクト)をコピーする場合、単純に「=」で代入するだけでは不十分な場合があります。
参照型変数の中身は参照先(オブジェクトのメモリ領域を示すポインタ)です。
「=」で代入するだけでは、参照先だけがコピーされて、参照しているものは同じという状態になるので、コピー先の変更がコピー元に影響してしまいますし、その逆にコピー元の変更がコピー先に影響してしまいます。

これを避けたい場合は、cloneメソッドを用いて中身を丸ごとコピー(新たにメモリ領域を確保し書き込み)する必要があります。
以下の記述を行うことで、cloneメソッドを使用することができるようになります。

・cloneしたいクラスでCloneableインターフェースを実装する

・Cloneableインターフェースのclone()メソッドをオーバーライドする

・clone()メソッド内で、super.clone()メソッドを使用する

 (super=Objectクラス)

・super.clone()メソッドはObject型で返ってくるので自身の型でキャストを行う

・CloneNotSupportedExceptionが返ってくる可能性があるので例外処理する

以下、サンプルコードです。
参照先のみコピーした場合とcloneで中身をコピーした場合を比較しています。
参照先のみコピーした場合は、コピー後にコピー先を変更した際にコピー元が影響を受けていますが、cloneで中身をコピーした場合は影響を受けていません。

【サンプルコード】

・CloneableItem.java

// cloneしたいクラスはCloneableインターフェースを実装する必要がある
public class CloneableItem implements Cloneable {

    // イミュータブルではない参照型変数を含む場合はそれも一緒にcloneが必要
    // (今回は割愛)
    private int itemId;
    private String itemName;

    public int getItemId() {
        return itemId;
    }
    public void setItemId(int itemId) {
        this.itemId = itemId;
    }
    public String getItemName() {
        return itemName;
    }
    public void setItemName(String itemName) {
        this.itemName = itemName;
    }

    // Cloneableインターフェースのclone()メソッドをオーバーライド
    @Override
    public CloneableItem clone() {
        CloneableItem clonedItem = null;
        // CloneNotSupportedExceptionを返す可能性があるので例外処理が必要
        try {
            // Object型で返ってくるのでキャストが必要
            clonedItem = (CloneableItem)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clonedItem;
    }

}

・ItemCloneMain.java

public class ItemCloneMain {
    public static void main(String[] args){

        System.out.println("[参照先のみコピーした場合]");
        CloneableItem cloneableItem1 = new CloneableItem();
        cloneableItem1.setItemId(1);
        cloneableItem1.setItemName("sword");
        System.out.println
            ("コピー前のコピー元オブジェクト:ID=" +
             cloneableItem1.getItemId() +
             " NAME=" +
             cloneableItem1.getItemName());
        CloneableItem cloneableItem2 = cloneableItem1;
        System.out.println
            ("コピー後のコピー元オブジェクト:ID=" +
             cloneableItem1.getItemId() +
             " NAME=" +
             cloneableItem1.getItemName());
        System.out.println
            ("コピー後のコピー先オブジェクト:ID=" +
             cloneableItem2.getItemId() +
             " NAME=" +
             cloneableItem2.getItemName());
        cloneableItem2.setItemId(2);
        cloneableItem2.setItemName("shield");
        System.out.println
            ("編集後のコピー元オブジェクト :ID=" +
             cloneableItem1.getItemId() +
             " NAME=" +
             cloneableItem1.getItemName());
        System.out.println
            ("編集後のコピー先オブジェクト :ID=" +
             cloneableItem2.getItemId() +
             " NAME=" +
             cloneableItem2.getItemName());

        System.out.println("");
        System.out.println("[cloneで中身をコピーした場合]");
        CloneableItem cloneableItem3 = new CloneableItem();
        cloneableItem3.setItemId(3);
        cloneableItem3.setItemName("armor");
        System.out.println
            ("コピー前のコピー元オブジェクト:ID=" +
             cloneableItem3.getItemId() +
             " NAME=" +
             cloneableItem3.getItemName());
        CloneableItem cloneableItem4 = cloneableItem3.clone();
        System.out.println
            ("コピー後のコピー元オブジェクト:ID=" +
             cloneableItem3.getItemId() +
             " NAME=" +
             cloneableItem3.getItemName());
        System.out.println
            ("コピー後のコピー先オブジェクト:ID=" +
             cloneableItem4.getItemId() +
             " NAME=" +
             cloneableItem4.getItemName());
        cloneableItem4.setItemId(4);
        cloneableItem4.setItemName("helm");
        System.out.println
            ("編集後のコピー元オブジェクト :ID=" +
             cloneableItem3.getItemId() +
             " NAME=" +
             cloneableItem3.getItemName());
        System.out.println
            ("編集後のコピー先オブジェクト :ID=" +
             cloneableItem4.getItemId() +
             " NAME=" +
             cloneableItem4.getItemName());

    }
}

【実行結果】

[参照先のみコピーした場合]
コピー前のコピー元オブジェクト:ID=1 NAME=sword
コピー後のコピー元オブジェクト:ID=1 NAME=sword
コピー後のコピー先オブジェクト:ID=1 NAME=sword
編集後のコピー元オブジェクト :ID=2 NAME=shield
編集後のコピー先オブジェクト :ID=2 NAME=shield

[cloneで中身をコピーした場合]
コピー前のコピー元オブジェクト:ID=3 NAME=armor
コピー後のコピー元オブジェクト:ID=3 NAME=armor
コピー後のコピー先オブジェクト:ID=3 NAME=armor
編集後のコピー元オブジェクト :ID=3 NAME=armor
編集後のコピー先オブジェクト :ID=4 NAME=helm

いかがでしたでしょうか。

javaでオブジェクトの中身をコピーしようとした場合、言語仕様上の問題により意外と複雑な実装になってしまいます。
そのため、サンプルコードを参考に実装するのがお勧めです。
Webには他の方が書いてくださったサンプルコードが色々とあるので、用途に応じて他のサンプルコードも参考にしてみると良いでしょう。

今回の記事がお役に立てれば幸いです。
これからもjavaに関する記事を書いていこうと思います!

コメント

タイトルとURLをコピーしました