【Go言語】埋め込んだ構造体のフィールドやメソッドを同階層にあるとして扱える話

構造体の埋め込みについて

例えば人を定義するとき、その構造体の中に「住所」という情報を混ぜることになったとします。このとき、以下のように構造体の中で構造体を利用することになります。これを構造体の埋め込みと言います。

package main

import "fmt"

type Address struct {
    Street     string
    City       string
    PostalCode int
}

type Person struct {
    FirstName string
    LastName  string
    Age       int
    Address
}

フィールドへのアクセス

このとき、Addressの中のStreetフィールドの値を利用するときは以下のようにアクセスすると思います。

func main() {
    p := Person{
        FirstName: "John",
        LastName:  "Doe",
        Age:       30,
        Address: Address{
            Street:     "123 Main St",
            City:       "Anytown",
            PostalCode: 12345,
        },
    }

    fmt.Println(p.Address.Street)
}

しかし、Go言語ではこのようにPerson構造体を宣言すると、インスタンス利用時には埋め込んだAddress構造体のフィールドがあたかも同階層で宣言されたかのようにアクセスすることが可能になります。

// 以下は同じ結果になる

fmt.Println(p.Address.Street)
// 123 Main St

fmt.Println(p.Street)
// 123 Main St

これはプロパティだけではなく、メソッドでも同じようにアクセスすることが可能になります。

Address構造体にGetAddress()をメソッドとして定義します。 この場合も同様に同階層にあるようにアクセスすることが可能になります。

func (a Address) GetAddress() string {
    return fmt.Sprintf("%s, %s, %d", a.Street, a.City, a.PostalCode)
}

func main() {
    p := Person{...

    // 以下2つは同じ結果
    fmt.Println(p.Address.GetAddress())
    fmt.Println(p.GetAddress())
}

複数の埋め込みと同名のフィールドがある場合

同名のフィールドがある場合は上記のようにアクセスすることはできなくなりエラーになります。 以下の場合コンパイルエラーになります。

type Address struct {
    Street     string
    City       string
    PostalCode int
}

type Person struct {
    FirstName string
    LastName  string
    Age       int
    Address
    ContactInfo
}

type ContactInfo struct {
    Email       string
    PhoneNumber string
    City        string
}

func main() {
    p := Person{...

    // Error : ambiguous selector p.City compiler(AmbiguousSelector)
    fmt.Println(p.City)
}

そのため上記のような場合はフィールド・メソッドともにどの構造体のフィールドにアクセスするのかを明示する必要が発生します

fmt.Println(p.Address.City)
fmt.Println(p.ContactInfo.City)

個人的には同階層として扱える状況でもどの構造体のフィールドにアクセスしているのか明示するほうが可読性が向上すると思いました