LoginSignup
0
0

immutableのメリットまとめ

Last updated at Posted at 2023-03-25

mmutableとimmutable

immutablemmutableは対になっており、それぞれメリットとデメリットがあります。
immutableは「不変」、mmutableは「可変」の意味を持っています。

「可変」の方が、つまりmmutableの方が変更しやすくて良さそうに思いますが、その変更しやすさのゆえバグの温床になります。

配列と文字列

一般に配列は操作可能で、文字列は操作不可能です。

ただしここでの操作は、変数の操作(再代入)ではなく、データそのものの操作に関してです。
下記が例です。

プリミティブであるStringに関しては、"my name"の操作ができません。

class User {
    private val name: String = "my name"
    private val friends: MutableList<String> = mutableListOf("matuo", "tanaka", "hamada")
    
    fun updateName() {
        name.append("hoge") // nothing method
    }
    
    fun appendFriend() {
        friends.add("sakata") // ok
    }

}

ただしvarを利用することで、再代入が可能になります。

class User {
    private var name: String = "my name"
    
    fun updateName() {
        name = "hoge" // ok
    }
}

ちなみにkotlinでは、mmutableとimmutableのlistがそれぞれ存在しています。
collectionはimmutableで、mutableListはmmutableです。

immutableなクラスとmmutableなクラス、その問題点

次に、mmutableなクラスを考えてみます。

メンバ変数をvarで定義したので、変数が再代入可能という意味でmmutableになっています。

class User(name: String, age: Int) {
    var name: String
    var age: Int
    
    init {
        this.name = name
        this.age = age
    }
      
}

同一インスタンスのメンバ書き換え

次に下記のコードを実行し、同一インスタンスに対して名前の書き換えを行います。

    val user = User("hamada", 35)
    println("${user.hashCode()}") // 598446861
    println("${user.name}: ${user.age}") // hamada: 35
    user.name = "tanaka"
    println("${user.hashCode()}") // 598446861
    println("${user.name}: ${user.age}") // tanaka: 35

同一ハッシュ(インスタンス)に対して、名前の書き換えが行われています。
これは問題でしょうか。

確かに問題ではあるものの、名前の書き換えを行うことは実際に起き得るので「書き換えが簡単に行われている」ことが確かに問題です。

変数の代入によるメンバ書き換え

次に同じ例を用いて下記のコードを実行します。

    val user = User("hamada", 35)
    println("${user.name}: ${user.age}") // hamada: 35

    val user2 = user 
    user2.name = "asada"
    println("${user2.name}: ${user2.age}") // asada: 35
    println("${user.name}: ${user.age}") // asada: 35

val user2 = user によって参照先が渡され、user2の変更がuserに影響してしまっています。
新しいユーザーを作成したつもりが、mmutableであるが故に意図しない変更が起こりうるのです。

解決策: immutableなクラスを生成する

今回の本題のimmutableなクラスに関してです。

kotlindata classでメンバをval定義します。
これだけでひとまずimmutableなクラス定義は完了です。

data class User(val name: String, val age: Int)
val user = User("hamada", 35)
user.name = "tanaka" // Val cannot be reassigned

新しいtanakaユーザーを作成したい場合はインスタンスをさらに作成する必要があります。

data class User(val name: String, val age: Int)
val user1 = User("hamada", 35)
val user2 = User("tanaka", 25)

immutableなクラスの値の更新について

メンバにvalをつけて、再代入を不可にすることでimmutabilityを獲得しました。
しかし、※1楽観的更新でユーザーの情報を更新したい場合にはどうすれば良いのでしょうか。

data classにはcopyメソッドが用意されているのでそちらを利用しましょう。

    var user = User("hamada", 35)
    println("${user.name}: ${user.age}") // hamada: 35
    var newUser = user.copy(name = "tanaka")
    println("${user.name}: ${user.age}") // hamada: 35
    println("${newUser.name}: ${newUser.age}") // tanaka: 35

data classは他にもequalsメソッドで等値比較(参照比較ではなく)ができるので、宣言的UIにはとても便利です。

flutterではfreezedパッケージを用いて、簡単にimmutabilityを獲得できます。
(コンパイルは伸びます)

※1 楽観的更新
サーバーに通信して、帰ってきた情報からインスタンスを再生成すればimmutableでも更新できますが通信前にimmutableクラスを更新したい場合です。

まとめ

immutabiltyは大事です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0