🤔

つまり「Why is there no type inheritance?」の答えは?

に公開

TL;DR

ポリモーフィズムを実現する手段として、Goのinterfaceの方が、継承よりも優れていると考えたから。

Who is this article for?

Goの公式ドキュメント「Frequently Asked Questions (FAQ)」には「Why is there no type inheritance?」という項があります。以下に引用します。

Why is there no type inheritance?

Object-oriented programming, at least in the best-known languages, involves too much discussion of the relationships between types, relationships that often could be derived automatically. Go takes a different approach.
Rather than requiring the programmer to declare ahead of time that two types are related, in Go a type automatically satisfies any interface that specifies a subset of its methods. Besides reducing the bookkeeping, this approach has real advantages. Types can satisfy many interfaces at once, without the complexities of traditional multiple inheritance. Interfaces can be very lightweight—an interface with one or even zero methods can express a useful concept. Interfaces can be added after the fact if a new idea comes along or for testing—without annotating the original types. Because there are no explicit relationships between types and interfaces, there is no type hierarchy to manage or discuss.
It's possible to use these ideas to construct something analogous to type-safe Unix pipes. For instance, see how fmt.Fprintf enables formatted printing to any output, not just a file, or how the bufio package can be completely separate from file I/O, or how the image packages generate compressed image files. All these ideas stem from a single interface (io.Writer) representing a single method (Write). And that's only scratching the surface. Go's interfaces have a profound influence on how programs are structured.
It takes some getting used to but this implicit style of type dependency is one of the most productive things about Go.

なぜGoには継承がないのかという問いに対して、Goのinterfaceの利点が回答として述べられています。

これを読んで、問われたことに真正面から答えていないと感じる方もいるでしょう。この記事は、そのような違和感を感じた方に向けて、この回答の意図を汲むための補足を提供します。

回答が腑に落ちないのは継承の本質を誤解しているから

この回答に違和感を感じる方は、おそらく継承の本質を「コードの再利用」と誤解されているのではないでしょうか。そのため、なぜGoには継承がないのかという問いに対して、コードの再利用とは直接関係のないGoのinterfaceの利点を回答として述べられると、腑に落ちない違和感を覚えるのだろうと想像します。

この違和感は、継承の本質に対する誤解を解くことで解消できるかもしれません。以降で、継承の本質について考えてみましょう。

継承の本質は再利用ではない

親クラスのメソッドは、子クラスで再利用できる——確かにこれは継承の利点です。しかし、これはあくまでも継承のいち利点に過ぎず、本質ではありません。なぜならば、コードの再利用は委譲(コンポジション)でも実現できるからです。つまり、単に再利用だけを目的とするならば、必ずしも継承でなくてもよいのです。よって、継承の本質は、コードの再利用にあるわけではありません。

では、何を目的とした場合に継承が必要になるのでしょうか? 継承にしかできないこととは何でしょうか? つまり、継承の本質は何でしょうか?

それは、ポリモーフィズムです。

継承の本質はポリモーフィズム

継承の本質は、「ある型を親型として扱うことができる」——つまり、ある型を介して、異なる型を同一視できるという点にあります。これがポリモーフィズム(多態性)です。例えば、Javaで「Animal」というクラスを作り、「Dog」や「Cat」がそれを継承していれば、List<Animal>にすべての動物を詰めることができます。これは型安全にして動的な振る舞いの切り替えを可能にします。

なお、ポリモーフィズムは関数ポインタなどでも実現できなくはありませんが、安全な型システムからは逸脱します。よって、ポリモーフィズムを型システムの中で安全に実現するためには、型の継承が必要です——少なくとも、従来のOOPではそうでした。

ポリモーフィズムを実現する新たな選択肢

ここで件のFAQに話を戻します。

回答は、継承の本質がポリモーフィズムであることを念頭に置いています。つまり、継承でなくてもよい理由をポリモーフィズムの観点で説明しようと試みています。そのため、ポリモーフィズムを実現するための新たな選択肢として、Goのinterfaceの利点を挙げているのです。

要するに、ポリモーフィズムを実現する手段としてGo独自のinterfaceという優れものを発明したので、もはや継承を持ち出す必要がない、というのが回答の趣旨です。

結論

Goに継承がないのは、ポリモーフィズムを実現する手段として、Goのinterfaceの方が、継承よりも優れていると考えたからです。継承の本質を念頭に置くことで、回答の意図をこのように汲むことができます。

Discussion