Javaで、ある変数が独自クラスのオブジェクトを参照しているとき、参照先の値が変更されると参照元の値も変更されてしまうが、Cloneableインターフェースを実装し、cloneメソッドをオーバーライドすることで、変更前の値をもつオブジェクトのコピーを取ることができる。
今回は、Cloneableインタフェースを使って独自クラスのオブジェクトのコピー(clone)を利用したサンプルプログラムを作成してみたので、共有する。
前提条件
下記記事の「IntelliJ IDEA上でSpring Bootプロジェクトの読み込み」まで完了していること。
IntelliJ IDEA上でGradleを使ってWeb画面のSpring Bootプロジェクトを作成してみたSpring Bootのプロジェクトを新規作成を「IntelliJ IDEA」のメニューから実施しようとしたところ、無料の「Commun...
サンプルプログラムの作成
作成したサンプルプログラムの構成は以下の通り。
なお、上記の赤枠は、今回作成・変更したプログラムである。
「build.gradle」の内容は以下の通りで、lombokを利用するための設定を追加している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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メソッドをオーバーライドしている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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の値の変化が見れるようにしている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | 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メソッドを呼び出し設定する必要がある。