もともと
object.method();
という表記は、methodがobjectのクラスに属しているという意味合いが強いかと思います。言葉を変えるとクラスとはデータと、そのデータを扱うコードが合わさったものという発想です。これはこれで一つの考え方で構いませんが、問題は全てをメソッドにするのも間違いだということです。その一例がダブルディスパッチということになります。以下のように加算演算子+を考えた場合、
object1 + object2
+演算子は
1.object1に所属するのか?
2.object2に所属するのか?
3.両方か?
4.どちらでもないか?等々
1-4の内どう考えるのが自然なのか?という問題に突き当たります。Javaは演算子のオーバーロードを禁止することによりこの問題から背を向けました。つまりこの問題を避けているとも言えます。ちなみにこのような仕様を見たときにJavaは補助輪が付いた自転車のような印象を受けました。C++は『どちらにも属さない』書き方ができます。正確にいうと『object1に属する』の書き方も場合によってはできますが、例えば、
10 + object
のような場合は、『どちらにも属さない』書き方でしか書けません。つまり関数を使ってオーバーロード演算子の定義を行います。C#はある意味『どちらにも属さない書き方』で書きます。つまり、多くのオブジェクト厨が忌嫌うpublic staticメソッドでオーバーロード演算子の定義を行います。
ダブルディスパッチの話を終える前にマルチディスパッチ(マルチメソッド)の話を軽くします。前回、2つのオブジェクトに対してポリモーフィズムを働かせる仕組みをダブルディスパッチと説明しましたが3つ以上のオブジェクトに対してポリモーフィズムを働かせる考え方もちろんあり、マルチディスパッチまたはマルチメソッドと言います。マルチメソッドをいつ使うのか?と言われたらそれはそれで考え込んでしまうのですが、それでも、メソッドが何処かのクラスに属するという考え方に固執するとこのようなパラダイムを受け入れることは難しいでしょう
さて、このようなある種の一般化を行った後は、今度は全くクラスに属さない手続き=関数という存在も自然に受け入れることができるかと思います。つまり、
y = sin(x);
のような式において、文字通りの関数、sinはどのクラスにも属さないと考えた方がよいでしょう。「Mathクラスに入れたらいいだろう」と反論を受けそうですが、実際にsin関数はJava厨が忌嫌うpublic staticで定義されており、C++でも関数として定義されています。先の演算子の例でも出てきましたが、public staticメソッドとは手続型言語での関数定義に相当するものになります。ちなみにかのεπιστημηさんは私との議論で、
Java/C#はどのクラスにも属さないメソッドが作れないため、
当たり障りのないクラス(たとえばMathあるいはModuleC)をでっちあげてそいつに押し込まざるを得ない。
「いかなるクラスにも属さない」という"思い"を表現できません。
僕にはそこが(C++と比べて)不自由に感じます。
というふうに本質的に関数であるものを仮のクラス(Math)に入れることには難色を示されています、これについては私も同感です。
さて、『そんなに関数が重要ならsinとかではなくもっと幅広く使える例を出せ!』と言われそうです。その答えは来週に行うということで。