第29回 シャローコピーとディープコピー
今回のテーマは「シャローコピーとディープコピー」です。
まずは「シャローコピーとは何ぞや」というところから始めましょう。 シャロー(shallow )とは「浅い」という意味です。 では、シャローコピーはいったい何を「浅い」としているのでしょうか。 この疑問を解決するために、前回説明したcloneメソッドを利用して説明していきます。 というのは、JavaのObjectクラスのcloneメソッドの実装はシャローコピーだからです。 前回cloneメソッドを使用した際にシャローコピーが行われていたのですね。
それでは前回と似たようなソースで確認していきましょう。 まずはいつものSportsインターフェースとその具象クラスです。
public interface Sports { void play(); } public class Badminton implements Sports { @Override public void play() { System.out.println("バドミントンして遊びます。"); } }
Playerクラスには、シャローコピーを説明しやすくするために、getterを一つ追加しました。
前回と同じくCloneableインターフェースをインプリメントしています。
public class Player implements Cloneable { private Sports sports; public Player(Sports sports) { this.sports = sports; System.out.println("コンストラクタです。"); } public Sports getSports() { return sports; } public void play() { System.out.println("ウォームアップします。"); sports.play(); System.out.println("クールダウンします。"); } }
Mainクラスでは実際に複製を行い、シャローコピーを理解するためのインスタンスの比較を行っています。 なお、今回は汎用的なclone生成メソッドを用意しました。 最後のキャスト時に型の安全性に関して、コンパイラから警告が出されてしまうので、SuppressWarningsアノテーションにより警告されないようにしています。 cloneメソッドがオーバーライドされていない限り、シャローコピーされます。
import java.lang.reflect.Method; public class Main { public static void main(String[] args) throws Exception { Player player = new Player(new Badminton()); player.play(); System.out.println(); Player clonePlayer = createClone(player); clonePlayer.play(); System.out.println(); System.out.print("Playerのインスタンスが同じ:"); System.out.println(player == clonePlayer); System.out.print("Sportsのインスタンスが同じ:"); System.out.println(player.getSports() == clonePlayer.getSports()); } @SuppressWarnings("unchecked") public static <T extends Cloneable> T createClone(T t) throws Exception { if (t == null) { return null; } Method cloneMethod = Object.class.getDeclaredMethod("clone"); cloneMethod.setAccessible(true); return (T) cloneMethod.invoke(t); } }
このプログラムを実行したら、複製を行ったPlayerのインスタンスは異なりますが、PlayerクラスのフィールドのSportsのインスタンスが同じという結果が出ます。 どうしてこうなるのでしょうか。
実はシャローコピーの場合、int等のプリミティブ型はそのままコピーされますが、参照型に関しては、あくまで参照がコピーされるだけであって、参照先のインスタンスがコピーされるわけではないのです。 じゃあ、参照先のインスタンスごとコピーする場合は何かというと、それがディープコピーなのです。 参照をコピーするのか、参照先のインスタンスごとコピーするのかについて、浅い深いと言っているわけですね。
シャローコピーとディープコピーの違いをしっかり理解して、状況に応じて適切に使い分けられるようになりましょう。