問題の歴史:
Javaでは、コレクションの不変性はCollections.unmodifiableList()のようなラッパーを介して、手動でimmutableクラスを設計(finalフィールド、セッターの不在)することで達成されます。Androidやサーバー開発の実践ではこれが使いづらく、不便になり、スレッドの安全性やデータの予測可能性が失われました。
問題:
Javaのコレクションはデフォルトで安全ではなく、常にオブジェクトへの参照を取得して変更することが可能です。これにより、不変性の管理が困難になり、特にマルチスレッド環境では微妙なバグが発生します。
解決策:
Kotlinは最初からread-onlyとmutableコレクションの分離を設計し、真の不変オブジェクトの作成がval、data class、およびmutable接頭辞のないList/Set/Mapを使うことで簡単になりました。これに加えて、copy()メソッドを使ったコピーの利用も可能です。
コード例:
val nums: List<Int> = listOf(1, 2, 3) // 不変リスト // nums.add(4) // コンパイルエラー val user = User(name = "Alex", age = 30) val updated = user.copy(age = 31) // 不変マップの例 val state: Map<String, Int> = mapOf("a" to 1, "b" to 2)
重要な特徴:
valは内部オブジェクトの絶対的な不変性を保証しますか?
いいえ!valは変数が他のオブジェクトを指さないことしか保証しませんが、オブジェクトのフィールドは変更可能です(内部プロパティがvarの場合)。
read-onlyコレクションからミュータブルオブジェクトを取得できますか?
はい — コレクションがミュータブルオブジェクトを含んでいる場合、read-onlyインターフェースのコレクションにもかかわらず、その状態を変更することができます。
data class User(var name: String) val users: List<User> = listOf(User("Vasya")) users[0].name = "Petya" // 許可される
List<Int>をas?でMutableList<Int>に変換するとどうなりますか?
コンパイルはできますが、元の型がread-onlyインターフェースだった場合、実行時にClassCastExceptionが発生します。
Androidアプリケーションはval List<User>で状態を保持し、それを不変であると考えています。開発者の1人がuser.nameを変更します。別のフラグメントで更新されたリストが予期しない不整合なデータを表示 — バグがリリース時に発生します。
長所:
短所:
valフィールドのみを持つimmutable data classを使用し、内部オブジェクトへのアクセスをフィルタリングし、copy()を通じて状態を更新します。
長所:
短所: