4 全てはオブジェクト

4 全てはオブジェクト

Scala は純粋なオブジェクト指向ですが、それは数や関数も含めて 全て がオブジェクトであるという意味においてです。この点において Java とは異なります。というのは Java ではプリミティブ型 (例えば boolean や int) と参照型とを区別しており、また関数を値として扱えないからです。

4.1 数はオブジェクト

数もオブジェクトなのでメソッドを持ちます。実際、下記のような式

1 + 2 * 3 / x

はメソッド呼び出しだけから成り立っています。それは前節で見たように下記の式と等価だからです。

(1).+(((2).*(3))./(x))

これは、+, * なども Scala では有効な識別子だ、ということも意味しています。

数字の周りの括弧は必要です。というのは、Scala の字句解析はトークンに対して最長一致規則を使うからです。ですから、下記の式

1.+(2)

は、トークン 1.、+、2. に分解されます。このようにトークン化されるのは、1. が 1 よりも有効な長い一致となるからです。トークン 1. はリテラル 1.0 として解釈され、Int ではなく Double を形づくります。

(1).+(2)

と式を書けば、1 が Double として解釈されるのを防げます。

4.2 関数はオブジェクト

多分 Java プログラマがより驚くことは、関数もまた Scala ではオブジェクトだということでしょう。従って関数を引数として渡したり、変数に格納したり、他の関数からの戻り値にしたりできます。関数を値として扱うこの能力は、 関数プログラミング と呼ばれる大変興味深いプログラミング・パラダイムの基礎の一部です。

なぜ関数を値として用いることが有用であるか、の非常に簡単な例として、1秒毎に何かアクションを行うタイマー関数について考えてみましょう。行うアクションをどのように渡せば良いでしょうか? 関数として、がきわめて論理的です。このように関数を渡す簡単な例は、多くのプログラマはよくご存知でしょう。ユーザインタフェイスのコードにおいて、何かイベントが起こった時に呼び出されるコールバック関数を登録する際によく使うからです。

下記のプログラムで、タイマー関数は oncePerSecond と呼ばれ、コールバック関数を引数として取ります。この関数の型は () => Unit と書かれ、引数無しで戻り値無しである全ての関数の型です ( Unit 型は C/C++ の void に似ています) 。このプログラムの main 関数は単にこのタイマー関数を、端末に文章を表示するコールバック関数を付けて呼び出すだけです。別の言い方をすれば、このプログラムは1秒毎に "time flies like an arrow" という文章を表示し続けます。

object Timer { def oncePerSecond(callback: () => Unit) { while (true) { callback(); Thread sleep 1000 } } def timeFlies() { println("time flies like an arrow...") } def main(args: Array[String]) { oncePerSecond(timeFlies) } }

文字列を表示するために System.out のメソッドではなく、あらかじめ定義された println メソッドを使っている、ということに留意して下さい。

4.2.1 無名関数

このプログラムは理解しやすいですが、もう少し洗練できます。まずはじめに、関数 timeFlies は、後で onecePerSecond 関数に渡すためだけに定義されていることに留意して下さい。関数に名前を付けるのは、一度きりしか使わないなら不必要と思われます。実のところ oncePerSecond に渡すためだけにこの関数を作成できればよいでしょう。Scala では 無名関数 、その名の通り名前の無い関数、を使えば可能です。私たちのタイマープログラムを timeFlies の代わりに無名関数を使って書き直すと、このようになります。

object TimerAnonymous { def oncePerSecond(callback: () => Unit) { while (true) { callback(); Thread sleep 1000 } } def main(args: Array[String]) { oncePerSecond(() => println("time flies like an arrow...")) } }

この例で無名関数を使っていることは、関数の引数リストと本体を分離している右矢印 '=>' によって判ります。引数リストが空であることは、矢印の左側の空の括弧の組で判ります。関数の本体は上の timeFlies と同じです。