今scalaの勉強してる。で、var, val, mutable, immutableで混乱しそうになったのでまとめる。
前提
- varは変数で参照を変えられる
- valはfinal変数的なもので参照を変えられない
- mutableは変更可能であることを示す(var, valのような文法上のキーワードではない)
- immutableは変更不可能であることを示す(var, valのような文法上のキーワードではない)
内容
例としてMapを使う。scalaのMapには
- scala.collection.mutable.Map
- scala.collection.immutable.Map
の2種類がある。ここで、代入とかそこらへんの仕組みを確かめてみる。
var x mutable
scala> var map = scala.collection.mutable.Map(1 -> "one")
map: scala.collection.mutable.Map[Int,java.lang.String] = Map(1 -> one)
scala> map += 2 -> "two"
res9: scala.collection.mutable.Map[Int,java.lang.String] = Map(1 -> one, 2 -> two)
ここの map += 2 -> "two"
は map.+=(2 -> "two")
と同義である。これはmap
変数の参照しているアドレスは変えずに、そのアドレスに格納されている値を変更している。また、+=
メソッドの返り値が変更された自分自身なので次の行に res9: scala.collection.mutable.Map[Int,java.lang.String] = Map(1 -> one, 2 -> two)
と出力されている。
var x immuable
scala> var map = scala.collection.immutable.Map(1 -> "one")
map: scala.collection.immutable.Map[Int,java.lang.String] = Map(1 -> one)
scala> map += 2 -> "two"
scala> map
res8: scala.collection.immutable.Map[Int,java.lang.String] = Map(1 -> one, 2 -> two)
ここの map += 2 -> "two"
はimmutableの場合と違い map = map + 2 -> "two"
と同義である。これは参照しているアドレスを変更している。
scalaでは代入式の返り値はUnitであるため、演算をした直後が空行になっている。
val x mutable
scala> val map = scala.collection.mutable.Map(1 -> "one")
map: scala.collection.mutable.Map[Int,java.lang.String] = Map(1 -> one)
scala> map += 2 -> "two"
res10: map.type = Map(1 -> one, 2 -> two)
これは var x mutable の場合と同じ。mapの参照しているアドレスは変わってないのでエラーにならない。
val x immutable
scala> val map = scala.collection.immutable.Map(1 -> "one")
map: scala.collection.immutable.Map[Int,java.lang.String] = Map(1 -> one)
scala> map += 2 -> "two"
<console>:9: error: reassignment to val
map += 2 -> "two"
^
この場合はエラーになる。var x immutable と同様、map += 2 -> "two"
は map = map + (2 -> "two")
であり、map変数の参照しているアドレスを変更しようとしている。しかし、mapはvalで宣言されているため、それが不可能でありエラーが発生している。