メソッドとフィールド

パラメーターなしメソッドと空括弧メソッド

Scalaにはパラメーターなしメソッドと空括弧メソッドがある。
言葉を聞いてもどっちがどっちだか分からないくらいややこしい。
パラメーターなしメソッドは、メソッドの定義の際に、仮引数がないうえにそれを囲む括弧も記述しない。
空括弧メソッドは、仮引数はないが空の括弧は記述する。
以上は定義の際の話。
パラメーターなしメソッドは、空括弧つきで呼び出すことはできない。
空括弧メソッドは、空括弧つきでもなしでも呼び出せる。
実にややこしい。
どの組み合わせが許されるのか、実験しないと分からないじゃないか。

(01) def met01 = println("OK")    // met01: Unit
(02) def met02() = println("OK")  // met02: Unit

(03) met01   // OK
(04) met02   // OK
(05) met01() // error: met01 of type Unit does not take parameters
(06) met02() // OK

ふーん。
もう一例。まずメソッド定義。

(07) def met03 = {println("OK");5}      // met03: Int
(08) def met04() = {println("OK");5}    // met04: ()Int
(09) def met05 = ()=>{println("OK");5}  // met05: () => Int

実行。

(09) var m = met03()  // error: met03 of type Int does not take parameters
(10) var m = met04()  // OK m: Int = 5
(11) var m = met05()  // OK m: Int = 5

(12) var m = met03  // OK m: Int = 5
(13) var m = met04  // OK m: Int = 5
(14) var m = met05  //m: () => Int = 

この例ではmet04とmet05が空括弧付きで実行すると同じ結果になる。
つまり実行して5をmに代入する。
が、括弧を省略すると結果が異なる。
met05の場合、関数オブジェクトがmに代入されるのである。
つまりメソッドの定義方法によっては、空の括弧を付けるか付けないかで意味が異なってくる。
使用者は、内部の実装を見ないとどう解釈されるか分からないことになる。
こんな仕様でいいのか?
実行時の括弧は省略不可にすべきだと思う。

defとval・var

メソッドの定義や呼び出しで括弧をつけないとなると、見掛け上、defはvalやvarとまったく同じ記述になる。
さらに、valやvarには関数値を代入できるので、逆にdefの定義と同じようなこともできてしまう。
結局、Scalaのdefとvalの違いは何だ?
Scalaは最終的にJavaのバイトコードになるので、Javaの言葉でいうと、defはJavaのメソッド、valやvarはJavaのフィールドになる。
とんでもなく当たり前の話だ。
Javaではメソッドはインスタンスには表れないので、格納する場所が違うということは予想できる。
defはクラス領域に、valやvarはインスタンスにできると思われる。
が、それはあくまで実装上の話で、言語仕様とはあまり関係がない。
Javaではメソッドとフィールドはまったく別物であり、目的も異なるが、Scalaではほとんど同じように使えるので、違いが分かり難い。
実装に関することを除けば、変更不可能なものを代入している場合、defとvalはほとんど区別できないと思う。

(01) def d1 = 5 // d1: Int
(02) val v1 = 5 // v1: Int = 5

上のように露骨な例でなくても、例えば関数リテラルを代入する例などでも同じだろう。

(03) def d1 = (x:Int, y:Int)=>x + y // d1: (Int, Int) => Int
(04) val v2 = (x:Int, y:Int)=>x + y // v1: (Int, Int) => Int = <function>

違いが出るのは、次のように変更可能なものを代入している場合のようだ

(05) var a = 1
(06) def d3 = a   // d3: Int
(07) val v3 = a   // v3: Int = 1
(08) a = 2
(09) println(d3)  // 2
(10) println(v3)  // 1

上の場合(07)のvalはvarに変えた方がdefと同じ結果になる可能性が生まれるかもしれないが、当然結果は変わらない。