1. 導入:なぜ読み取り専用が必要なのか?
Java開発において、メソッドの引数やクラスのフィールドでコレクションを渡す際、意図せず値が書き換えられてしまうバグは非常に厄介です。これを防ぐために「読み取り専用ビュー(Unmodifiable wrappers)」を使用します。この技術を使うことで、データの不変性(イミュータビリティ)を保証し、予期せぬ副作用を排除した堅牢なコードを実現できます。
2. 基礎知識:読み取り専用ビューとは?
Javaのコレクションフレームワークで提供される「読み取り専用ビュー」とは、元のコレクションへの参照を持ちつつ、addやremoveといった変更操作を禁止するラッパーのことです。
重要なのは、これらは「コピー」ではなく「ビュー(覗き窓)」であるという点です。つまり、元のコレクション側で要素が追加されると、その変更は読み取り専用ビュー側にも反映されます。あくまで「このビューを経由した変更を禁止する」仕組みであることを理解しておきましょう。
3. 実装と解決策
Javaの標準ライブラリであるjava.util.Collectionsクラスには、主要なコレクションに対応したUnmodifiableメソッドが用意されています。
・Collections.unmodifiableList(List)
・Collections.unmodifiableSet(Set)
・Collections.unmodifiableMap(Map)
これらに変換することで、呼び出し元が不注意でデータを改ざんしようとした際に、実行時例外(UnsupportedOperationException)を発生させ、早期にバグを発見できるようになります。
4. サンプルプログラム
以下のコードで、読み取り専用ビューの挙動を確認してみてください。
import java.util.;
public class UnmodifiableExample {
public static void main(String[] args) {
// 元となるリストを作成
List
mutableList.add("Java");
mutableList.add("Python");
// 読み取り専用ビューを作成
List
// 読み取りは可能
System.out.println("リスト内容: " + readOnlyList);
try {
// ここで変更を試みると例外が発生する
readOnlyList.add("C++");
} catch (UnsupportedOperationException e) {
System.out.println("警告: 読み取り専用リストへの変更は許可されていません。");
}
// 注意点: 元のリストを変更すると、ビュー側も変わってしまう
mutableList.add("Go");
System.out.println("元のリスト変更後のビュー: " + readOnlyList);
}
}
5. 応用・注意点:現場で役立つアドバイス
現場で活用する上で、特に注意すべきポイントが2点あります。
1つ目は、真の不変性を求めるなら「List.copyOf()」を使うことです。Java 10以降で導入されたList.copyOf()(およびSet.copyOf, Map.copyOf)は、元のコレクションの「コピー」を作成した上で不変にします。そのため、元のコレクションが後から変更されても影響を受けません。より安全なコードにするには、こちらを優先的に検討しましょう。
2つ目は、「構造」は不変でも「中身」は変えられる点です。リスト内にミュータブルなオブジェクト(例えば自作のEntityクラスなど)を入れている場合、リスト自体を変更できなくても、リスト内のオブジェクトのプロパティを書き換えることは可能です。完全に不変にしたい場合は、保持するクラス自体も不変オブジェクト(recordなど)にするよう心がけてください。

コメント