構造体の埋め込みの基本
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 35/107。
構造体の埋め込み (Struct embedding) は、ある構造体型を別の構造体型の中に含めるための Go の仕組みです。フィールド名を使用する代わりに、単に型を指定するだけで、Go は埋め込まれた構造体のフィールドを外部の構造体に昇格 (promote) させます。
基本的な構文は以下の通りです。
type Address struct {
City string
Country string
}
type Person struct {
Name string
Address // 埋め込み - フィールド名なし
}
Address を Person に埋め込むと、外側の構造体から埋め込まれたフィールドに直接アクセスできるようになります。
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は埋め込まれた構造体のフィールドを自動的に昇格させるため、あたかも外部の構造体に属しているかのようにアクセスできるようになります。必要に応じて、埋め込まれた型名を通じてそれらにアクセスすることも可能です。
これは、内部フィールドにアクセスするために常にフィールド名を使用しなければならない通常のネストされた構造体とは異なります。埋め込み(Embedding)は、関連データの論理的なグループ化を維持しながら、よりフラットで便利なAPIを作成します。
チャレンジ
簡単構造体の埋め込み(struct embedding)を使用して、クリーンで整理されたデータモデルを作成する車両登録システムを構築しましょう。埋め込みによってすべてのフィールドへの直接アクセスを可能にしながら、連絡先情報と車両の詳細を分離します。
コードは2つのファイルに分けて構成します:
vehicle.go: 埋め込みを通じて連携する2つの構造体を作成します:OwnerNameとPhoneフィールド(ともに文字列)を持つContactInfo構造体ContactInfoを(フィールド名なしで)埋め込み、独自のMake、Model、Yearフィールドを追加する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)を実演してください:
- Vehicle から直接アクセスした所有者名(例:
v.OwnerName) - 埋め込まれた型を通じてアクセスした電話番号(例:
v.ContactInfo.Phone) Summaryメソッドからの完全なサマリー
例えば、Alice Smith、555-1234、Toyota、Camry、2023 が与えられた場合、出力は以下のようになります:
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
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー