メインコンテンツにスキップ
モジュール 5 · レッスン 3 / 5コース内の 23/50~12 min
モジュールのレッスン (3/5)

合成(埋め込み)

Go には 継承はありません。再利用に対する慣用的な答えは次のとおりです。 構成、特に埋め込み: 1 つのタイプを含む フィールド名を指定せずに別の内部に追加します。フィールドとメソッド 埋め込み型は昇格され、あたかも直接アクセス可能になります。 彼らは外側のタイプに属していました。

構造体の埋め込み

Go
type Animal struct {
    Name string
}

func (a Animal) Greet() string {
    return "ciao da " + a.Name
}

type Dog struct {
    Animal        // embedding — NO field name
    Breed string
}

c := Dog{
    Animal: Animal{Name: "Rex"},
    Breed:  "Pastore",
}

fmt.Println(c.Name)         // "Rex" — promoted from Animal
fmt.Println(c.Greet())      // "ciao da Rex" — promoted method
fmt.Println(c.Breed)        // own field

埋め込みフィールドには 型の名前によって引き続きアクセスできます: c.Animal.Name は機能し、c.Name と同じです。こんなときに必要になります 衝突があります。

オーバーライド (部分的): メソッド「シャドウイング」

外部型がプロモートされた型と同じ名前のメソッドを定義している場合 1 つは、その方法が「勝つ」ということです。内部からいつでも組み込みにアクセスできます 1 つはタイプの名前によるものです。

Go
func (c Dog) Greet() string {
    return "BAU! " + c.Animal.Greet()
}

これはポリモーフィックな継承ではなく、単なる字句上の名前解決です。 ポリモーフィズムには インターフェイス (モジュール 6) を使用します。

複数の埋め込み

複数のタイプを埋め込むことができます。

Go
type Logger struct{ /*...*/ }
func (l Logger) Log(msg string) { /*...*/ }

type Timer struct{ /*...*/ }
func (t Timer) Now() time.Time { /*...*/ }

type Service struct {
    Logger
    Timer
    Name string
}

s := Service{Name: "api"}
s.Log("start")   // promoted from Logger
s.Now()          // promoted from Timer

2 つの埋め込み型に同じ名前のメソッドがある場合、直接アクセス 曖昧になり、コンパイラは明示的な形式を必要とします (s.Logger.Foo() または s.Timer.Foo())。

インターフェースの埋め込み

インターフェイスを「埋め込む」こともできます (モジュール 6 を参照)。

Go
type ReadWriter interface {
    io.Reader
    io.Writer
}

これは、Reader と Writer のメソッドの結合と同等です。

明示的な合成 (埋め込みではない) を使用する場合

名前付きフィールドが必要で自動昇格が必要ない場合は、プレーンを使用します 構成:

Go
type Dog struct {
    Anim  Animal    // NORMAL field, no promotion
    Breed string
}
c.Anim.Name   // you need the field name

埋め込みは便利な 委任ツール であり、設計上の選択ではありません 明示的な合成よりも厳密に優れています。本当に必要な場合に使用してください。 内部型の API を促進します。

試してみてください

運動#go.m5.l3.e1
試行回数: 0読み込み中…

Animal (埋め込み、フィールド名なし) を埋め込み、品種文字列フィールドを持つ Dog を定義します。

エディターを読み込み中…
ヒントを表示

埋め込み: フィールド名を指定せずに TYPE を記述します。

3 回の試行後に解決策が利用可能になります

運動#go.m5.l3.e2
試行回数: 0読み込み中…

Greet() 文字列メソッドを Animal に追加し、Dog 変数で直接呼び出します (メソッドのプロモーション)。

エディターを読み込み中…
ヒントを表示

Animal で定義されたメソッドは、埋め込みのおかげで Dog に昇格されます。

3 回の試行後に解決策が利用可能になります

Quiz#go.m5.l3.e3
準備完了

Go は古典的な継承をサポートしていますか?

Go
type B struct { /* extends A? */ }
回答の選択肢

要約

  • 継承なし: 埋め込みを使用して作成します。
  • 埋め込み = 構造体にフィールド名を付けずに TYPE を書き込むこと。
  • 埋め込み型のフィールドとメソッドは 昇格されます。
  • 明示的なアクセスは常に利用可能です: outer.Inner.Field。
  • 「字句」オーバーライド: 外側の型のメソッドは、プロモートされた型のメソッドをシャドウします。
  • 埋め込み ≠ is-a: ポリモーフィズムには インターフェイス を使用します。