Menu
Coddy logo textTech

構造体の埋め込みの基本

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

Struct embeddingは、ある struct 型の中に別の struct 型を含めるための Go の仕組みです。field 名を使用する代わりに、型を指定するだけで、Go は埋め込まれた struct の fields を外側の struct に昇格させます。

基本的な構文は次のとおりです。

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.Cityが、p.Address.Cityと明示的に記述しなくても機能することに注目してください。Goは埋め込まれた構造体のfieldsを自動的に昇格させ、外側の構造体に属しているかのようにアクセス可能にします。必要に応じて、埋め込み型の名前を通じて引き続きアクセスできます。

これは、内部の field にアクセスするために常に field name を使用する必要がある通常のネストされた構造体とは異なります。Embedding によって、関連するデータの論理的なグループ化を維持しながら、よりフラットで便利な API が作成されます。

challenge icon

チャレンジ

簡単

struct embedding を使用して、クリーンで整理されたデータモデルを作成する車両登録システムを構築しましょう。embedding によってすべての fields に直接 access できるようにしながら、contact information と vehicle details を分離します。

コードを2つのファイルに整理します。

  • vehicle.go: embedding を通じて連携する2つの struct を作成します。
    • ContactInfo struct:OwnerNamePhone の fields(どちらも string)を持ちます
    • Vehicle struct:ContactInfo を field name なしで embeds し、独自の MakeModelYear fields を追加します
    また、vehicle と contact のすべての information を含む formatted string を返す、VehicleSummary という method も作成します。
  • main.go: input から vehicle と owner の details を読み取り、embedded ContactInfo を持つ Vehicle instance を作成し、embedded fields に直接、または embedded type name を通じて access する方法を示します。最後に vehicle summary を出力します。

次の input が提供されます。

  • Line 1: Owner name
  • Line 2: Phone number
  • Line 3: Vehicle make
  • Line 4: Vehicle model
  • Line 5: Year(integer)

Summary method は、次の Format の string を返す必要があります。

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

main file では、次の内容を出力して field promotion を示します。

  1. Vehicle 上で直接 access した owner name(例:v.OwnerName
  2. embedded type を通じて access した phone(例:v.ContactInfo.Phone
  3. Summary method による full summary

たとえば、Alice Smith555-1234ToyotaCamry2023 が与えられた場合、出力は次のようになります。

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

field promotion による直接 access と、embedded type name を通じた明示的な access の両方の access パターンが機能するため、embedded data を扱う方法に柔軟性が得られることに注目してください。

自分で試してみよう

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腕試し

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

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

自分で練習してみよう: Goオンラインコンパイラ