Go言語触ってみた その3

今週も、Goを触ってみて気になった点とかについてまとめてみる。

先週の振り返り

先週はGoの型システムについてブログを書いた。新しい型を定義する際に、

  1. 既存の型に別名(?)をつける場合
  2. struct型を定義する場合
  3. interface型を定義する場合

の3通りの方法があると述べた。

今週は、先週ちょろっと触れながら詳しくは説明しなかったembedding typeについて触れる。

embedding typeとは?

Goにおいては、structやinterfaceを定義する際にembedding(埋め込み)を行う事が出来る。このとき、structとinterfaceそれぞれでembeddingの意味合いは異なってくるので順に説明する。

interfaceにおけるembedding

interfaceにおけるembeddingの方が単純で分かり易いので、まずこちらについて説明する。

interafaceは「特定のメソッドが存在する事を保証する仕組み」な訳だけど、interfaceにおいてembeddingを行う事で、埋め込まれたinterfaceの性質を受け継ぐ事が出来る。

例えば、ReaderとWriterというinterface型が存在する時に

type ReadWriter interface {
    Reader
    Writer
}

というReadWriter型の定義があると、ReaderとWriterの両方の性質を満たす値をReadWriter型として受け取る事が出来る様になる。

すなわち、例えば次の例の様にReader型の定義がReadメソッドが定義されている事、Writer型の定義がWriteメソッドが定義されてる事とすると、ReaderとWriterを埋め込んだReadWriter型はReadメソッドとWriteメソッド両方が定義された型という事になる。

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

// この時、上で定義したReadWriter型は次の様に定義したものと同じになる
type ReadWriter interface {
    Read(p []byte) (n int, err error)
    Write(p []byte) (n int, err error)
}

文字通り、 interface型にinterface型が埋め込まれて いるのが分かる。

structにおけるembedding

interfaceにおけるembeddingは分かり易かったが、structにおけるembeddingはちょっとややこしい。

interfaceにおいては埋め込まれるのはinterfaceのみであったが、structにおいては任意の型を埋め込む事が出来る。 例えば、interface型のReaderとWriterを埋め込んだStructReadWriter型を作る事が出来る。

type StructReadWriter struct {
    Reader // Readerはinterface型
    Writer // Writerはinterface型
}

このとき、StructReadWriter型の値はReaderやWriterをフィールドとして持ち、さらにReaderやWriterにただ処理を委譲するだけのReadメソッドやWriteメソッドを持つ事になる。すなわち、structにおける埋め込みを行った上記のコードは次のコードと等価である。

type StructReadWriter struct {
    Reader Reader // Readerというフィールド名でReader型の値を持つ
    Writer Writer // Writerというフィールド名でWriter型の値を持つ
}

func (srw StructReadWriter) Read(p []byte) (n int, err error) {
    return srw.Reader.Read(p) // ReaderフィールドのReadメソッドを呼び出し
}

func (srw StructReadWriter) Write(p []byte) (n int, err error) {
    return srw.Writer.Write(p) // WriterフィールドのWriteメソッドを呼び出し
}

structにおいてembeddingを行うと任意の型の実装を受け継ぐ事が出来るため、 継承 と同様の機能を果たす事が出来る。ただし、実際には継承とは異なる点に注意しなければならない。

まとめ

Go言語において、embedding typeは広く使われる。 作者達としては継承よりもより一般性の高いアプローチらしく、興味深く感じた。