Menu
Coddy logo textTech

構造体の埋め込みの基本

CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 35/107。

構造体の埋め込み (Struct embedding) は、ある構造体型を別の構造体型の中に含めるための Go の仕組みです。フィールド名を使用する代わりに、単に型を指定するだけで、Go は埋め込まれた構造体のフィールドを外部の構造体に昇格 (promote) させます。

基本的な構文は以下の通りです。

type Address struct {
    City    string
    Country string
}

type Person struct {
    Name string
    Address  // 埋め込み - フィールド名なし
}

AddressPerson に埋め込むと、外側の構造体から埋め込まれたフィールドに直接アクセスできるようになります。

func main() {
    p := Person{
        Name: "Alice",
        Address: Address{
            City:    "London",
            Country: "UK",
        },
    }
    
    fmt.Println(p.City)     // London - 直接アクセス
    fmt.Println(p.Country)  // UK - 直接アクセス
    fmt.Println(p.Address.City)  // こちらも動作します
}

p.Cityp.Address.City と明示的に記述しなくても動作することに注目してください。Goは埋め込まれた構造体のフィールドを自動的に昇格させるため、あたかも外部の構造体に属しているかのようにアクセスできるようになります。必要に応じて、埋め込まれた型名を通じてそれらにアクセスすることも可能です。

これは、内部フィールドにアクセスするために常にフィールド名を使用しなければならない通常のネストされた構造体とは異なります。埋め込み(Embedding)は、関連データの論理的なグループ化を維持しながら、よりフラットで便利なAPIを作成します。

challenge icon

チャレンジ

簡単

構造体の埋め込み(struct embedding)を使用して、クリーンで整理されたデータモデルを作成する車両登録システムを構築しましょう。埋め込みによってすべてのフィールドへの直接アクセスを可能にしながら、連絡先情報と車両の詳細を分離します。

コードは2つのファイルに分けて構成します:

  • vehicle.go: 埋め込みを通じて連携する2つの構造体を作成します:
    • OwnerNamePhone フィールド(ともに文字列)を持つ ContactInfo 構造体
    • ContactInfo を(フィールド名なしで)埋め込み、独自の MakeModelYear フィールドを追加する Vehicle 構造体
    また、すべての車両情報と連絡先情報を含むフォーマット済み文字列を返す、Vehicle 型の Summary というメソッドを作成してください。
  • main.go: 入力から車両と所有者の詳細を読み取り、ContactInfo を埋め込んだ Vehicle インスタンスを作成します。埋め込まれたフィールドに直接アクセスする方法と、埋め込まれた型名を通じてアクセスする方法の両方を実演してください。最後に車両のサマリーを出力します。

以下の入力が提供されます:

  • 1行目: 所有者名
  • 2行目: 電話番号
  • 3行目: 車両のメーカー (Make)
  • 4行目: 車両のモデル (Model)
  • 5行目: 年式 (Year、整数)

Summary メソッドは、次の形式の文字列を返す必要があります:

[Year] [Make] [Model] - Owner: [OwnerName], Phone: [Phone]

メインファイルでは、以下を出力することでフィールドの昇格(field promotion)を実演してください:

  1. Vehicle から直接アクセスした所有者名(例:v.OwnerName
  2. 埋め込まれた型を通じてアクセスした電話番号(例:v.ContactInfo.Phone
  3. Summary メソッドからの完全なサマリー

例えば、Alice Smith555-1234ToyotaCamry2023 が与えられた場合、出力は以下のようになります:

Alice Smith
555-1234
2023 Toyota Camry - Owner: Alice Smith, Phone: 555-1234

フィールドの昇格による直接アクセスと、埋め込まれた型名による明示的なアクセスの両方のパターンが機能することに注目してください。これにより、埋め込まれたデータを柔軟に扱うことができます。

チートシート

構造体の埋め込みを使用すると、フィールド名を指定せずに、ある構造体型を別の構造体型の中に含めることができます。埋め込まれた構造体のフィールドは外部の構造体に昇格され、直接アクセスできるようになります。

基本的な構文

type Address struct {
    City    string
    Country string
}

type Person struct {
    Name string
    Address  // 埋め込み - フィールド名なし
}

埋め込まれたフィールドへのアクセス

埋め込まれたフィールドには、2つの方法でアクセスできます。

p := Person{
    Name: "Alice",
    Address: Address{
        City:    "London",
        Country: "UK",
    },
}

// 直接アクセス(フィールドの昇格)
fmt.Println(p.City)     // London

// 埋め込まれた型名経由
fmt.Println(p.Address.City)  // London

Goは埋め込まれた構造体のフィールドを自動的に昇格させるため、それらが外部の構造体に属しているかのように直接アクセスできます。必要に応じて、埋め込まれた型名を通じてアクセスすることも可能です。

自分で試してみよう

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// 所有者の名前を読み取る
	ownerName, _ := reader.ReadString('\n')
	ownerName = strings.TrimSpace(ownerName)

	// 電話番号を読み取る
	phone, _ := reader.ReadString('\n')
	phone = strings.TrimSpace(phone)

	// 車両のメーカーを読み取る
	make, _ := reader.ReadString('\n')
	make = strings.TrimSpace(make)

	// 車両のモデルを読み取る
	model, _ := reader.ReadString('\n')
	model = strings.TrimSpace(model)

	// 年式を読み取る
	yearStr, _ := reader.ReadString('\n')
	yearStr = strings.TrimSpace(yearStr)
	year, _ := strconv.Atoi(yearStr)

	// TODO: ContactInfo を埋め込んだ Vehicle インスタンスを作成する
	// 上記で入力から読み取った値を使用する

	// TODO: Vehicle から直接アクセスした所有者名を出力する(フィールドの昇格)
	// 例: fmt.Println(v.OwnerName)

	// TODO: 埋め込まれた型名を通じてアクセスした電話番号を出力する
	// 例: fmt.Println(v.ContactInfo.Phone)

	// TODO: Summary メソッドを使用して完全な要約を出力する
	// 例: fmt.Println(v.Summary())

	// ソリューションを実装したら、これらの行を削除する
	_ = ownerName
	_ = phone
	_ = make
	_ = model
	_ = year
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン