2026年1月31日土曜日

【Java】シャローコピー vs ディープコピー:参照の罠からコードを守る

 Javaでオブジェクトをコピーする際、単に = で代入したり、標準の clone() を使ったりすると、思わぬバグを引き起こすことがあります。その原因である「シャローコピー(浅いコピー)」と、解決策としての「ディープコピー(深いコピー)」について解説します。



1. シャローコピー(Shallow Copy / 浅いコピー)


仕組み


シャローコピーは、オブジェクトの**「参照(メモリ上の住所)」だけ**をコピーします。

コピー元とコピー先は、メモリ上の同じ実体(インスタンス)を指している状態です。

Javaでの実装例


Java
class Person {
    String name;
    Person(String name) { this.name = name; }
}

public class Main {
    public static void main(String[] args) {
        Person alice = new Person("Alice");
        
        // シャローコピー(参照のコピー)
        Person copyAlice = alice; 

        copyAlice.name = "Bob"; // コピー側を書き換えると...

        System.out.println(alice.name);     // Bob(元の方も変わってしまう!)
    }
}

なぜ問題か?:意図せずオリジナルデータが破壊されるため、バグの特定が非常に困難になります。



2. ディープコピー(Deep Copy / 深いコピー)


仕組み


ディープコピーは、メモリ上に実体そのものを新しく作成し、値をコピーします。

コピー元とコピー先は完全に別の住所を持つため、互いに影響を与えません。

Javaでの推奨実装:コピーコンストラクタ


Java
class Person {
    String name;
    Person(String name) { this.name = name; }

    // コピーコンストラクタ(自分と同じ型を受け取って新しく作る)
    Person(Person other) {
        this.name = other.name; 
    }
}


3. 【深掘り】clone()メソッドは使わないのが正解?


Javaには標準で Object.clone() というメソッドが存在しますが、現在では**「使用を避けるべき」**というのが一般的なエンジニアの共通認識です。

なぜ clone() は避けられるのか?


  1. Cloneableインターフェースの設計不備: インターフェースなのにメソッドが定義されておらず、振る舞いが特殊。

  2. 例外処理の強制: 常に CloneNotSupportedException をキャッチする必要があり、コードが汚れる。

  3. シャローコピーのリスク: デフォルトの super.clone() はシャローコピーを行うため、結局自分で深いコピーの実装を書く手間が変わらない。


結論: 新しくコードを書くなら、コピーコンストラクタか、ファクトリメソッドを作成するのがJavaのベストプラクティスです。



4. 不変(Immutable)オブジェクトならコピーは不要


「コピーしなければならない状況」を物理的に無くすのが、最も賢い設計です。それが不変(Immutable)オブジェクトです。


仕組み


一度作成したら中身を変更できないクラスにすれば、シャローコピーをしても副作用が起きません。

  • Java標準の例: StringInteger など。

  • 自作の例: フィールドを final にし、セッター(setter)を作らない。

Java
public final class ImmutableUser {
    private final String name; // finalで書き換え禁止
    public ImmutableUser(String name) { this.name = name; }
    public String getName() { return name; }
}


メリット:

  • どこに渡しても中身が変わる心配がないため、コピーを作る必要自体がなくなります。

  • メモリ消費を抑えられ、スレッドセーフ(マルチスレッドでも安全)になります。



まとめ:使い分けの指針


状況採用すべき手法
基本の代入シャローコピー(ただし変更しないことが前提)
元データを保護して編集したいディープコピー(コピーコンストラクタ推奨)
クラスを自作する場合可能な限り「不変(Immutable)」にする

clone() の複雑さに悩まされるより、「不変にする」あるいは「新しく作る(コピーコンストラクタ)」というシンプルな戦略をとることが、堅牢なJavaプログラムへの近道です。



0 件のコメント:

コメントを投稿

【Java】Maven×H2で作る「消える」データベース環境:macOS Sequoiaでの検証ガイド

 Java開発において、データベース(DB)操作の習得は必須です。今回は Maven を使用してプロジェクトを構築し、アプリ終了と共にデータが消える H2インメモリデータベース を、Javaプログラムから制御する手順を解説します。 1. 開発環境(Mac) 今回の検証は以下の最新...