Go言語で「embedded で継承ができる」と思わないほうがいいのはなぜか？

この記事は [Go7 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/go7) 11日目の穴埋め記事です。


# 背景

以前、 @mattn さんが↓のようなツイートをされていまして、

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">embedded と継承は(似てる様な気がするのは分かりますが)一緒にしない方がいいと思います。<a href="https://t.co/zalv2y5TVG">https://t.co/zalv2y5TVG</a></p>&mdash; mattn (@mattn_jp) <a href="https://twitter.com/mattn_jp/status/1161975390614589440?ref_src=twsrc%5Etfw">August 15, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

その際に「ほんそれ！」と激しく同意しながらも、ではなぜ「embeddedを継承と思うべきではないのか」が自分の中でうまく言語化できていませんでした。この機会に整理しておこうと思った次第です。


# 復習：Embedded Field とは

次の `Mom` という struct を例に考えてみましょう:

```golang
type Mom struct {
	name string
}

func newMom(n string) *Mom {
	m := new(Mom)
	m.name = n
	return m
}

func (m *Mom) Hello() string {
	return m.name + "でございまーす"
}
```

`Mom` は `name` というフィールドだけを持つシンプルな struct です。また、文字列を返す `Hello` というメソッドを持っています。簡単ですね！

これに、次の `Kid` という struct を加えてみます。

```golang
type Kid struct {
	Mom             // ←これが embedded field
	nickname string // ←Kid独自のフィールド
}

func newKid(n, m string) *Kid {
	k := new(Kid)
	k.name = n
	k.nickname = m
	return k
}
```

`Kid` の定義では、フィールド名なしで `Mom` と型名だけが書かれています。これがembedded field です。メソッドは何も定義されていません。

で、これを次のようなmain関数で実行すると、

```golang
func main() {
	mom := newMom("サザエ")
	kid := newKid("タラオ", "タラちゃん")
	fmt.Println(mom.Hello())
	fmt.Println(kid.Hello())
}
```

このように表示されます：

```
サザエでございまーす
タラオでございまーす
```

([The Go Playground](https://play.golang.org/p/YrUUNvNCyWW)で実行する)

`Kid` にはなにもメソッドを実装していなかったのに、なんのエラーもなく `Hello()` が呼び出せていますね。しかもそれは `Mom` の `Hello` メソッドです。
そうです。 `Kid` は `Mom` を「継承」しているように見えますね。

では、ここで次のメソッドを追加してみましょう:

```golang
func (k *Kid) Hello() string {
	return k.nickname + "ですう"
}
```

そして再び上のmain関数を実行すると、

```
サザエでございまーす
タラちゃんですう
```

([The Go Playground](https://play.golang.org/p/HPlnQifT1CN)で実行する)

表示が変わりましたね。
はい、皆さんのご期待のとおり、`Hello` メソッドがオーバーライドされました。

ここまでくればもう皆さん、こんな顔↓してますよね？

![DnjoKaiU0AE7VaM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100577/51413137-8ee5-c757-71ff-f9548ad0465e.png)

これでもうGoでオブジェクト指向なプログラムをバリバリ書いちゃうぜ！って思うじゃないですか？[^1]でも、ここからが罠なんです。

[^1]: かくいう自分も、最初に [A Tour of Go](https://tour.golang.org/) を終えて「完全に理解した」顔をして、従来の発想のまま既存プログラムの移植をガーっと書き始めました。んで、300行くらいのクラス構成を書き上げてからコンパイルを通してみようとしてみて、「ん？これ全然アカンやん！」となって再入門したクチです。

## 罠１: フハハハハ、代入できると誰が言った！

「継承」のつもりで、当たり前のようにこんなコードを書くと、

```golang
var m *Mom
m = newKid("タラオ", "タラちゃん")
```

コンパイルエラーになります：

```
./prog.go:37:4: cannot use newKid("タラオ", "タラちゃん") (type *Kid) as type *Mom in assignment
```

([The Go Playground](https://play.golang.org/p/OQ7WdIx84z6)で実行する)

`Mom` 型の変数に `Kid` 型の値は代入できません。
こう言ったほうがいいかもしれませんね。**親の型の変数に派生型の値は代入できません。**[^2]

[^2]: 念のため、Go言語としては親の型とか派生型といった概念はないので、Goの世界においては不正確な表現です。

よくオブジェクト指向言語における継承は「is-a」関係だと言われるように、「子型 is a 親型」として親の型が使える場所では子の型が代わりに使えるんでした。でも、**Goではそれはできません。**

それでは embedded field のメソッドをオーバーライドしても多相性[^3]が実現できないじゃないか！と思われるでしょう。そうです、Go の **embedded field は多相性を提供しません。**（※Goで多相性を実現する方法は後述します）

[^3]: [ポリモーフィズム (Polymorphism)](https://ja.wikipedia.org/wiki/%E3%83%9D%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%95%E3%82%A3%E3%82%BA%E3%83%A0)、多態性とも。ここでは、部分型付け（部分型多相）のことをさす。要は、実行時の値によって、実際に呼ばれるメソッドが変わるアレです。

## 罠２: 継承したかと思った？残念！丸投げちゃんでした！

上の `Kid` の例を見ると、`Kid` は `name` と `nickname` の２つのフィールドを持っているように思えますね。もちろん、`name` は `Mom` から「継承」したフィールドです。

そう思うと、`Kid` を初期化するときに短くこうやって↓書きたくなるじゃないですか：

```golang
kid := &Kid{name: "タラオ", nickname: "タラちゃん"}
```

しかし、こんなふうに書こうものなら、

```
./prog.go:36:14: cannot use promoted field Mom.name in struct literal of type Kid
```

([The Go Playground](https://play.golang.org/p/cOg3vuISFh_t)で実行する)

コンパイラに怒られます。

ちゃんとコンパイルを通すためには、こう書きます：

```golang
kid := &Kid{
	Mom:      Mom{name: "タラオ"},
	nickname: "タラちゃん",
}
```

([The Go Playground](https://play.golang.org/p/Tg5ehzMm6lZ)で実行する)

なんか、突然 `Mom` という名前のフィールドが出てきましたね。これが embedded field です。
`Kid` は `Mom` の型名と同じフィールド名で、実際にフィールドに `Mom` 型の値を持っているんですね。そうです。**`Kid` と `Mom` は「is-a」関係ではなく「has-a」関係なのです**。

これまでの例では裏側で何が起こっていたのかというと、`kid.name` と書くとコンパイラが `kid.Mom.name` に読み替えてくれていたというわけです。**要は省略形です**。

同様に `kid.Hello()` も `kid.Mom.Hello()` と省略できるようにいいかんじにやってくれていただけです。コンパイラが、次のようなラッパーメソッドを自動的に生成してくれていると思ってもいいでしょう：

```golang
func (k *Kid) Hello() {
	k.Mom.Hello()
}
```

## 罠３: embeddedの前にオーバーライドなど無力！

もうおわかりのように、`Kid` は `Mom` から何も「継承」していません。ただ、同名のフィールド／メソッドの処理を `Mom` フィールドの値に**丸投げしているだけだったのです**。

従って、`Mom` にこんなメソッド↓を追加して、

```golang
func (m *Mom) Sing() string {
	return m.Hello() + "！お魚くわえたドラ猫～♪"
}
```

次のように実行すると、

```golang
func main() {
	mom := newMom("サザエ")
	kid := newKid("タラオ", "タラちゃん")
	fmt.Println(mom.Sing())
	fmt.Println(kid.Sing())
}
```

どう表示されると思います？
答えは次の通り：

```
サザエでございまーす！お魚くわえたドラ猫～♪
タラオでございまーす！お魚くわえたドラ猫～♪
```

([The Go Playground](https://play.golang.org/p/KLLvIqioYFD)で実行する)

どうです？予想通りでしたか。

`kid.Sing()` の中でも `Mom` の `Hello` の実装が呼び出されていますね。でも、 `Kid` は `Hello` をオーバーライドしたんじゃなかったでしたっけ？

これも、上記のラッパーの要領で読み替えればわかりやすいと思います：

`kid.Sing()` → `kid.Mom.Sing()`

そうです。`Sing` は `Kid` のメソッドではなく、`kid` のフィールドの値である `Mom` のメソッドなのですから、その中では `Mom` の `Hello` が呼び出されるのが正しいのです。


# 継承じゃないよ、委譲だよ

このように、embedded field は継承ではなくただの「丸投げ」だったわけですが、このように自分の処理を他のオブジェクトに丸投げするパターンを、オブジェクト指向言語ではよく「委譲（delegation)」と呼びます[^4]。

[^4]: [本来の用語としては「委譲」ではなく「転送 (forwarding)」が正しいという話もあります](https://qiita.com/jesus_isao/items/4b6b7846ccf5eb46b1bc)が、もはやこの用語で定着してしまったので、ここでは委譲で通します。

そうです。Goは言語機能として「継承」を取り入れず、「委譲」を言語機能として直接サポートするという選択をしたのですね。

[『Effective Java』でも語られているように](https://qiita.com/mikamikuh@github/items/1cdcd8b25a2e23f10525)、近年はオブジェクト指向言語でも「継承より委譲」と言われることが多くなっています。
ではなぜ、「継承より委譲」のほうが良いのでしょうか？

## 「実装の再利用」と「多相性」とを分離する

[Wikipedia によると](https://ja.wikipedia.org/wiki/%E7%B6%99%E6%89%BF_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0))、オブジェクト指向で「継承」を使う際には次の２つの目的で使われます：

> - コードの再利用
> - 派生型による共用と置換、多態性[^5]

[^5]: 原文ママ。多相性と同義。

私個人の見解ですが、「継承」はこの２つの機能が同時に入ってしまっていることが問題だと感じています。たとえば、既存コードを再利用したかっただけなのに、それに継承を使ってしまったために、思わぬ使われ方（派生元クラスのコンテキストで使われるとか、さらに派生先でオーバーライドされた結果カプセル化が壊れるとか）がされてしまうということが起こります。逆に、多相性が必要だっただけなのに、不必要な実装まで継承してしまうといったこともありえます。

本来「継承」は不必要なところに「継承」を使ってしまうと事故ることが多いので、実装の再利用には委譲を、多相性だけが必要なところにはインターフェイスを使おう、そして本当に必要なときにだけ継承を使おうね、というのがベストプラクティスとなっていったわけです。

一方でGoでは、実装の再利用はembedded fieldで、多相性はインターフェイスで実現する、と設計の段階から完全に分離しました。これは、不必要な場面で継承を使ってしまって事故るということが、設計上起こらないように工夫された結果なのですね。[^6]

[^6]: [昨年のアドベントカレンダーでは、Goは「例外」の役割を返却値の型と大域脱出の２つに分離したのだ](https://qiita.com/Maki-Daisuke/items/80cbc26ca43cca3de4e4) というような筋書きで１本書いたのですが、実は今回もそれと同じような話になっています。Goの言語設計は、言語が提供すべき機能を注意深く分解して、ひとつの言語要素が複数の機能をまぜて提供してしまわないように本当に注意深く設計されていると本当に感心します。これが、Goが学びやすく、そして失敗をおかしにくい言語になっている秘訣だと思っています。その一方で「なんとこれはこんなことにも使えます！」みたいな驚きがないため、そういうトリッキーな派手さ／便利さが好きな人からは魅力的に映らないんだろうな、というのもよくわかります。

## 「再利用の使者・embedded」「多相性の使者・interface」「ふたりはプリ(ry」

しかしもちろん、この２つが分けて提供されていたとしても、組合せて使うことで「継承のようなこと」が実現できるように工夫されています。

例として、`Kid` も自分流の自己紹介をしてから歌うように上の例を修正してみましょう。

まず、多相性を使うためインターフェイスを定義します：

```golang
type Helloer interface {
	Hello() string
}
```

この `Helloer` は `Hello` メソッドを持っている値すべてを表します。

そして `Sing` はこの定義した `Helloer` を引数に受け取る関数として次のように定義します：

```golang
func Sing(h Helloer) string {
	return h.Hello() + "！お魚くわえたドラ猫～♪"
}
```

そしてこれを次のような `main` 関数で実行しますと：

```golang
func main() {
	hs := []Helloer{
		newMom("サザエ"),
		newKid("タラオ", "タラちゃん"),
	}
	for _, h := range hs {
		fmt.Println(Sing(h))
	}
}
```

次のように、ちゃんと期待通りの結果になりましたね：

```
サザエでございまーす！お魚くわえたドラ猫～♪
タラちゃんですう！お魚くわえたドラ猫～♪
```

([The Go Playground](https://play.golang.org/p/XZMBZXoZtUb)で実行する)

というわけで、Goで「継承のようなこと」をするには、embedded だけでは足りません。embedded と interface、この２つが揃ってはじめて「継承のようなこと」ができる、とおぼえておいてくださいね！