javaでは、以下のように
int a = 0; int b = ++a; System.out.print(a); // 1 が表示される System.out.print(b); // bも1
++というインクリメントするための演算子があります。
しかしscalaの場合
var a = 0 // a ++ これはダメ val b = a += 1 print(a) // 1 が表示される print(b) // b はUnit
というようにしないといけません。
この違いについてのおなはし(・ω・)ノ
これをちゃんと解説しようとすると、かなりややこしいんですが、それなりの理由があります。
すごく細かいところだけど、Javaとの違いで初心者が戸惑いやすいところだと思います。
Javaの言語仕様書の15.14 〜 15.18あたりだとおもいますが、++、+=、--、-=、などは、すべてOperator(演算子)として説明されてますよね?
しかし、Scalaには演算子は存在せず、すべてがメソッドです。scalaでは、
1 + 2
というのは、
1 .+(2)
とも書けます。
1というオブジェクトの、+というメソッドを呼び出し、その引数として2を渡す
という意味です。
なぜインクリメントの++というものが存在しないかというと、
- javaでのprimitive型も、scalaでは普通のオブジェクトと統一的に扱う
- つまり、演算子が存在せず、(IntやCharなどJavaではprimitive型と呼ばれているものも)すべてがオブジェクトであり、すべてがメソッド
という原則を優先したためです。
つまり、iがInt型のとき、++iや、i++という構文を許してしまうと、これらをメソッドを呼び出すという構文で解釈できないからです。
javaの場合のインクリメント
int i = 0; int b = ++i;
が意味するところは、
- i という変数の値を1増やす
- かつ、 ++i という式全体としてもi+1した値を返す
という感じですかね。
しかし、scala的な考え方をすると、
- var i = 0 というのは、"iという変数に0というIntのオブジェクトをbind(束縛)した" ということになる
- Intというのは、immutableな(変更不可能で状態を持たない)オブジェクトである
- よって、iという変数に再代入しない限り、iに束縛された0のメソッドを呼んでも、iは0のままであるはず
ということになります。
ここでいいたいのは、
インクリメントって、一度束縛された変数の値を、代入以外の方法で違うものに書き換えてるよね?しかもprimitive型しかそういう操作できないし。それってなんか統一的じゃなくね?
ってことです。整理すると以下のようになります
Java | インクリメントやデクリメントなどの操作を行うことで、変数に違うオブジェクトを束縛しなおすことができる |
---|---|
scala | 代入以外の方法では、変数への束縛ができない |
ではなぜscalaで
var i = 0 i += 1
とした場合に、i (という変数に束縛される値)が1になるのか?というと、以下のような操作
var i = 0 i = i + 1
のシンタックスシュガーだからです!
これがシンタックスシュガーというのは、コップ本*1323、324ページあたりに解説されています。以下引用
+= メソッドをサポートしていないaを使って a += bというコードを書くと、Scalaはa = a + bと解釈しようとしてみるのだ。
+=メソッドだけでなく、=で終わるあらゆるメソッドに同じ考えが適用される。
ところで、このシンタックスシュガーはコレクションだけでなく、あらゆるタイプの値で機能する。
このような効果は、Javaの+=,-=,*=などの代入演算子と似ているが、Scalaでは=で終わるすべての演算子で可能であり、この効果はより一般的なものとなる。
コップ本にもあるように、scalaでは、メソッド名が=で終わるものは特別扱いされます。それがどういうところで使うと便利なのかっていう詳細は、もう説明つかれたのでコップ本嫁ってことで・・・(´・ω・`)
たとえば、このルールがすべてのモノに適用されるので、(説明のための例で、実用的にはあまり意味がないですが)以下のようなことができます↓*2
scala> case class Test(val a:String){ | | def ++++(b:String) = Test(b) | | } defined class Test scala> val x = Test("hoge") x: Test = Test(hoge) scala> x ++++= "fuga" // xはvalなので再代入できない <console>:9: error: reassignment to val x ++++= "fuga" ^ scala> var y = Test("hoge") y: Test = Test(hoge) scala> y ++++= "fuga" // y = y.++++("fuga") のシンタックスシュガー scala> y res1: Test = Test(fuga) // yが Test(fuga) に置き換わってる
このscalaの考え方がすごくわかりやすいか?っていうとどうなんだろう、ってところはありますが・・・コップ本にあるように、collectionのimmutableなものと、mutableなものの切り替えなんかで有用だったりします。