ミュータブル=死亡の世界
考察
- プロパティに
set
や再代入したら死 - 配列やコレクションに構造的変更を加えると死
data class
を積極的に使おう- ミュータブルなクラスのプロパティをメンバーにしたが最後死
なぜ人はイミュータブルなオブジェクトを好むのか?
- 状態変化が存在し得ず、バグを潜在的に予防できるから
- MTセーフだから (Rustも言っているように、ミュータブルなオブジェクトを並行で触ってしまったら大変なことになる)
Make it immutable in Kotlin
A1: data class と val のあわせ技
data class Person(val age: Int, val address: StructedAddress)
シンプル・イズ・ベストという言葉通りである。暗黙のうちに、指定されたパラメーターのみ変化させた新しいインスタンスを生成するcopy()
関数と、宣言順でタプルのように分解宣言をすることができるcomponentN()
が定義される。さらに、equals()
とhashCode()
も暗黙のうちに生成されるといたれりつくせりである。 (もちろん、明白に定義されていればそちらを優先する)
A2: ミュータブルなオブジェクトを包み直す
class ImmutablePerson(private val delegate: MutablePerson): IPerson {
fun setAge(v: Int) {
unsupported()
}
fun getAge(): Int {
return delegate.age
}
fun setAddress(v: StructedAddress) {
unsupported()
}
fun getAddress(): StructedAddress {
return delegate.address
}
fun unsupported(): Nothing {
throw UnsupportedOperationException("Cannot call mutable method")
}
}
委譲 というテクニックで、継承よりも良いとされている。具体的には、万が一MutablePerson
がfinal
になったとしても、コードの変更が不要という点だ。
A3: 継承してしまう
class ImmutablePerson(age: Int, address: StructedAddress) : MutablePerson(age, address) {
override fun setAge(v: Int) {
unsupported()
}
override fun getAge(): Int {
return super.age
}
override fun setAddress(v: StructedAddress) {
unsupported()
}
override fun getAddress(): StructedAddress {
return super.address
}
fun unsupported(): Nothing {
throw UnsupportedOperationException("Cannot call mutable method")
}
}