Goでis-a関係を作る

Goには継承はなく、実装の再利用は構造体の埋め込みで行う。じっさいは委譲だが、見た目はオブジェクト指向言語で継承した場合のように使える。

package main

type Super struct {}
func (super Super) SuperMethod() {}

type Sub struct {Super}

func main() {
    sub := Sub{Super{}}
    sub.SuperMethod() // Subから直接Superのメソッドを実行できる
}

ただしこれだと当然is-a関係にはならないため、以下のようにSuperを要求する関数でSubは使えない。

package main

func RequiresSuper(super Super) Super {
    return super
}

type Super struct {}
func (super Super) SuperMethod() {}

type Sub struct {Super}

func main() {
    sub := Sub{Super{}}
    sub.SuperMethod()

    super := RequiresSuper(sub) // コンパイルエラーになる
    super.SuperMethod()
}

sub.Superを渡すことで済む場合もあろうが、当然それはSuper型の変数となってしまうので、今度はSubのメソッドを実行できない。

package main

func RequiresSuper(super Super) Super {
    return super
}

type Super struct {}
func (super Super) SuperMethod() {}

type Sub struct {Super}

func main() {
    sub := Sub{Super{}}
    sub.SuperMethod()

    super := RequiresSuper(sub.Super) // ここを変更
    super.SuperMethod()
    super.SubMethod() // コンパイルエラー
}

どうするか。インターフェイスを使う。SuperSuperInterfaceを満たすようにし、Superを直接でなく、SuperInterfaceを要求するようにする。

package main

func RequiresSuper(super SuperInterface) SuperInterface {
    return super
}

type SuperInterface interface { SuperMethod() }

type Super struct {}
func (super Super) SuperMethod() {}

type Sub struct {Super}
func (sub Sub) SubMethod() {}

func main() {
    sub := Sub{Super{}}
    sub.SuperMethod()
    sub.SubMethod()

    super := RequiresSuper(sub)
    super.SuperMethod()
    super.(Sub).SubMethod() // とはいえ、SuperInterfaceはSubMethod()を当然持っていないので、ここではどうしても型表明が必要になる
}

ここまで辿り着くのにかなりかかった。