ポリモーフィズムは継承の面白い副作用..なんかじゃない

なんか微妙な話の流れに。

3. 同時に考えなければならないことを減らせること

(中略)

オブジェクト指向プログラミングの学習にスコープを移せば、ポリモーフィズムと継承が絡まない動的言語を使って学んだ方が、格段に楽だと思います。ポリモーフィズムのことを考えるときはポリモーフィズムのことだけ、差分プログラミングのことを考えるときには差分プログラミングのことだけを考えていられます。

難しい言語 - みねこあ

に、

ポリモフィズムと継承は表裏一体なので,片方だけを考えるのはむしろおかしい.

「難しい言語」の補足 - カレーなる辛口Javaな転職日記

ときて、同コメント欄で、

>ポリモフィズムと継承が表裏一体の概念
えーっと,「表裏一体の概念」とは書いていませんよね.「表裏一体で使用する」の方が適切かと.定義上は関係ない概念であったとしても,実際に使用する上で表裏一体で使用するので十分に柔軟で実用的であるという経験則があるわけです.

「難しい言語」の補足 - カレーなる辛口Javaな転職日記

となっていることに対して。

最初の引用について私が言いたかったことを、もう一度、もっと解りやすくまとめてみようと思いました。(自分のために)


* * *


最初に事実を確認。動的型付けな OOPL では、実際に使用する上でも これら(継承とポリモーフィズム)はかならずしも表裏一体で使用しません。動的型付けOOPL が好きな人って「だから好き」って人が多いと思います。また、JavaScript のようなプロトタイプベースなOOPL でもポリモーフィズムは出来ますし、VBScript のような クラスベースだけれどクラス継承のない OOPL でもポリモーフィズムは出来ます。「実際に使用する上で表裏一体で使用する」ってことは決して無いです。



さて。ポリモーフィズムと差分プログラミングは 別のものです。

だってね。継承してもオーバライドしないとポリモーフィズムしないのですよ。どうせ上書きされるなら、実装は継承されていないわけです。ならば意味があるのはメソッドシグネチャだけでしょう。それが「インターフェイス」。

サブクラスのインスタンススーパークラス型と見なして使うとき、オーバーライドしてないものがそのメソッドを備えているのはポリモーフィズムでもなんでもなくって、これは単なる「差分プログラミング」です。

この二つはどう考えたって違うものジャン。事実 Interfaceの無い C++ だって最初からこれらを違うものとして扱うことなんか常識です。昔のことは知らない猫ですが、実装と仕様の分離なんて手垢の着いている概念だとおもうのです*1。でもね、構文がいっしょで似ていると、ついゴッチャにしちゃうし、ゴッチャになることで混乱してわけがわからなくなっちゃう。だから OOPはオブ脳だとかOOPはサトるものだとか言われちゃう。

だから Javaはいい。だって Javaインターフェイスを Interface で表現できるし、

class Foo extends Bar implements Buzz{
}

と、「Foo は Bar を拡張し、Buzz を実装している」とその意味を区別して書くから。私が Java を 学びやすい言語と感じているのはココです。ユーザに 別のものを別のものとして意識させる工夫が有ります。私はJavaを少々たしなむ程度にしか知らないから 単なる思い込みかもしれないけれど、Java はこのような混乱からプログラマを救おうとしている風に感じます。それがいい。


* * *


でも動的型付けOOPLなら、本当に別のものとして扱えるから(この話に限っては)もっといいです。

インターフェイス(メソッドシグネチャの集合)を手で定義してこれを型と明示する。インターフェイスを備えていることを示すのに、この型のサブタイプであると明示的に記述する。これが継承。

しかし、そうではなくって、オブジェクトの使われ方(メソッドの呼ばれ方)をみれば、ポリモーフィズム出来るか出来ないかが解るから、それを言語処理系で事前にチェックしたり、実行時に「とりあえずやってみる」という手段でも、別段ポリモーフィズムするには困りません。

インターフェイス(必要なメソッドシグネチャの集合)は、継承するものではなくなります。

これが、

ポリモーフィズムと継承が絡まない動的言語

で言いたかったことで、なので継承は差分プログラミングのときのみに使うことになるから、二つの違う概念を「継承」が橋渡ししちゃったりしない、

ポリモーフィズムのことを考えるときはポリモーフィズムのことだけ、差分プログラミングのことを考えるときには差分プログラミングのことだけ

というのが自然に出来るから混乱しないですむ、という主張でした。


* * *


私は OOP のキモはポリモーフィズムだと考えていて、だからOOPを教えるには、クラス継承を教えずにポリモーフィズムだけを教えられる動的型付け言語のほうが効率が良いと考えています。

実際 チームのOOP初めて導入のときに そのように教えてみたのですが、効率が良かったと感じています*2

私はそういう風に考えているから 「簡単な言語」「学びやすい言語」の条件に、

  • 別の概念を別の概念として扱えること。兼ねない。
  • 同時に考えなければならないことを減らせること

があるんじゃないかな、と書いたのでした。

*1:いまどきの典型的なOOPLでは、クラス継承で 実装とインターフェイスの両方を問答無用で継承してしまいますが、たとえば C++ では 実装だけを継承できる private継承なんてのがあります。あんまり使いどころ無いですが(^^;

*2:だたし、これをやったのはお仕事言語がC++だったから、C++だったら「急がば回れだ」だ!と思ったけれど、C#Javaだったら回り道はしなかったと思います。(というか C#のときはしなかったです)