Javaで、ある変数が独自クラスのオブジェクトを参照しているとき、参照先の値が変更されると参照元の値も変更されてしまうが、Cloneableインターフェースを実装し、cloneメソッドをオーバーライドすることで、変更前の値をもつオブジェクトのコピーを取ることができる。
今回は、Cloneableインタフェースを使って独自クラスのオブジェクトのコピー(clone)を利用したサンプルプログラムを作成してみたので、共有する。
前提条件
下記記事の「IntelliJ IDEA上でSpring Bootプロジェクトの読み込み」まで完了していること。
IntelliJ IDEA上でGradleを使ってWeb画面のSpring Bootプロジェクトを作成してみたSpring Bootのプロジェクトを新規作成を「IntelliJ IDEA」のメニューから実施しようとしたところ、無料の「Commun...
サンプルプログラムの作成
作成したサンプルプログラムの構成は以下の通り。

なお、上記の赤枠は、今回作成・変更したプログラムである。
「build.gradle」の内容は以下の通りで、lombokを利用するための設定を追加している。
plugins {
id 'org.springframework.boot' version '2.1.7.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//lombokを利用するための設定
compileOnly 'org.projectlombok:lombok:1.18.10'
annotationProcessor 'org.projectlombok:lombok:1.18.10'
}
Contractクラス・Userクラスの内容は以下の通りで、cloneメソッドによるオブジェクトのコピーを行えるようにするため、Cloneableインタフェースを実装し、cloneメソッドをオーバーライドしている。
package com.example.demo;
import lombok.Data;
// cloneメソッドによるオブジェクトのコピーを行うため、Cloneableインタフェースを実装し、
// cloneメソッドをオーバーライドする
@Data
public class Contract implements Cloneable{
/** 郵便番号 */
private String postCode;
/** 住所 */
private String address;
/** 電話番号 */
private String phone;
@Override
public Contract clone(){
// cloneメソッドをオーバーライドする
// 今回はString型しかないため、単純に親クラスのcloneメソッドを呼び出す
try {
return (Contract) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}package com.example.demo;
import lombok.Data;
// cloneメソッドによるオブジェクトのコピーを行うため
// Cloneableインタフェースを実装し、
// cloneメソッドをオーバーライドする
@Data
public class User implements Cloneable{
/** ID */
private int id;
/** 名前 */
private String name;
/** 連絡先 */
private Contract contract;
public User clone(){
// cloneメソッドをオーバーライドする
// 今回はオブジェクト(Contract)がメンバ変数に含まれているため、
// 親クラスのcloneメソッドを呼び出した後で、Contractクラスの
// cloneメソッドを呼び出し設定する
try {
User user = (User)super.clone();
user.contract = this.contract.clone();
return user;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}メインクラスの内容は以下の通りで、user1を変更したときの、(user1を直接参照している)user2と、(user1をcloneした)user3の値の変化が見れるようにしている。
package com.example.demo;
public class DemoMain {
/**
* cloneメソッドの動作を確認するメイン処理
* @param args 起動時に受け取る引数
*/
public static void main(String[] args){
User user1 = makeUser1();
User user2 = user1;
User user3 = user1.clone();
System.out.println("*** user1オブジェクト(変更前)の各値 ***");
System.out.println("user1 = " + user1.toString());
System.out.println("user2 = " + user2.toString());
System.out.println("user3 = " + user3.toString());
user1 = changeUser1(user1);
System.out.println("*** user1オブジェクト(変更後)の各値 ***");
System.out.println("user1 = " + user1.toString());
System.out.println("user2 = " + user2.toString());
System.out.println("user3 = " + user3.toString());
}
/**
* user1オブジェクト(変更前)を生成する
* @return user1オブジェクト(変更前)
*/
private static User makeUser1(){
User user1 = new User();
user1.setId(1);
user1.setName("テスト プリン1");
Contract contract1 = new Contract();
contract1.setPostCode("160-0023");
contract1.setAddress("東京都新宿区西新宿");
contract1.setPhone("03-1111-111X");
user1.setContract(contract1);
return user1;
}
/**
* 引数のuser1オブジェクトを変更する
* @param user1 user1オブジェクト(変更前)
* @return user1オブジェクト(変更後)
*/
private static User changeUser1(User user1){
user1.setId(2);
user1.setName("テスト プリン2");
Contract contract1 = new Contract();
contract1.setPostCode("173-0004");
contract1.setAddress("東京都板橋区板橋");
contract1.setPhone("03-2222-222Y");
user1.setContract(contract1);
return user1;
}
}
サンプルプログラムの実行結果
サンプルプログラム「DemoMain.java」を実行した結果は以下の通りで、赤枠のように、user1を変更すると、(user1を直接参照している)user2にはuser1の変更が反映されているが、(user1をcloneした)user3にはuser1の変更が反映されないことが確認できる。

要点まとめ
- 変更前の独自クラスのオブジェクトのコピー(clone)を作成できるようにするには、Cloneableインタフェースを実装し、cloneメソッドをオーバーライドする。
- 独自クラスにオブジェクトがメンバ変数に含まれている時は、親クラスのcloneメソッドを呼び出した後で、独自クラスのメンバ変数のcloneメソッドを呼び出し設定する必要がある。






