リフレクションの基本
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 83/107。
リフレクションを使用すると、プログラムは実行時に自身の構造を調べ、操作できます。Go の reflect パッケージはこの機能を提供し、型の検査、構造体のフィールドの読み取り、メソッドの動的な呼び出しを可能にします。
リフレクションにおける2つの基本的な型は、reflect.Type と reflect.Value です。これらは reflect.TypeOf() と reflect.ValueOf() を使って取得します。
import "reflect"
type Person struct {
Name string
Age int
}
p := Person{Name: "Alice", Age: 30}
t := reflect.TypeOf(p)
v := reflect.ValueOf(p)
fmt.Println(t.Name()) // Person
fmt.Println(t.Kind()) // struct
fmt.Println(v.NumField()) // 2構造体のフィールドを反復処理して、それらの名前、型、値を調べることができます。
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
fmt.Printf("%s: %v\n", field.Name, value)
}
// 出力:
// Name: Alice
// Age: 30Reflection は強力ですが、トレードオフも伴います。コンパイル時の型チェックを回避し、直接アクセスよりも遅く実行され、コードの理解を難しくします。JSON marshaling、ORM マッピング、またはコンパイル時に型がわからない汎用ユーティリティの構築などの作業に限定して使用してください。
チャレンジ
簡単reflection を使って任意の構造体を調べ、その構造を報告する構造体インスペクターを作成しましょう!これは reflection の実用的な使用例です。コンパイル時には未知の型を扱えるユーティリティを作成します。
コードを2つのファイルに分けて構成します。
inspector.go: reflection ベースの検査ユーティリティを作成します。渡された任意の構造体を
reflectパッケージで調べる関数InspectStruct(v any) stringを作成します。この関数は、次の内容を含む整形済みのレポートを返します。- 構造体の型名
- 構造体の kind("struct" である必要があります)
- フィールドの数
- 各フィールドについて、フィールド名、型、現在の値
出力形式は次のようにします。
Type: [TypeName] Kind: struct Fields: [count] - [FieldName] ([FieldType]): [Value] - [FieldName] ([FieldType]): [Value] ...型の情報を取得するには
reflect.TypeOf()を使用し、フィールドの値にアクセスするにはreflect.ValueOf()を使用します。NumField()を使ってフィールドを反復処理し、メタデータには型に対するField(i)を、実際の値には値に対するField(i)を使用します。main.go: サンプル構造体を定義し、インスペクターを使用します。構造体の型(
productまたはemployee)とそのフィールド値を読み取り、適切な構造体を作成して検査します。productの場合は、Name(string)、Price(float64)、InStock(bool)フィールドを持つProduct構造体を作成します。名前、価格、在庫状況を読み取ります。employeeの場合は、ID(int)、Name(string)、Department(string)フィールドを持つEmployee構造体を作成します。ID、名前、department を読み取ります。作成した構造体を
InspectStructに渡し、結果を出力します。
次の入力が与えられます。
- 1行目: 構造体の型(
productまたはemployee) - 続く行: その構造体の型に対応するフィールド値
例えば、次の入力が与えられた場合:
product
Laptop
999.99
true出力は次のようになります。
Type: Product
Kind: struct
Fields: 3
- Name (string): Laptop
- Price (float64): 999.99
- InStock (bool): trueまた、次の入力が与えられた場合:
employee
42
Alice Johnson
Engineering出力は次のようになります。
Type: Employee
Kind: struct
Fields: 3
- ID (int): 42
- Name (string): Alice Johnson
- Department (string): Engineeringさらに、次の入力が与えられた場合:
product
Headphones
79.50
false出力は次のようになります。
Type: Product
Kind: struct
Fields: 3
- Name (string): Headphones
- Price (float64): 79.5
- InStock (bool): falseインスペクターが、あらかじめ構造を知らなくても異なる構造体の型を扱えることに注目してください。これが reflection の力です!同じ InspectStruct 関数が、Product と Employee の両方の構造体を動的に処理します。
自分で試してみよう
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// Name、Price、InStockフィールドを持つProduct構造体
type Product struct {
Name string
Price float64
InStock bool
}
// ID、Name、Departmentフィールドを持つEmployee構造体
type Employee struct {
ID int
Name string
Department string
}
func main() {
reader := bufio.NewReader(os.Stdin)
// 構造体の型を読み取る
structType, _ := reader.ReadString('\n')
structType = strings.TrimSpace(structType)
if structType == "product" {
// productのフィールドを読み取る
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
priceStr, _ := reader.ReadString('\n')
priceStr = strings.TrimSpace(priceStr)
price, _ := strconv.ParseFloat(priceStr, 64)
inStockStr, _ := reader.ReadString('\n')
inStockStr = strings.TrimSpace(inStockStr)
inStock, _ := strconv.ParseBool(inStockStr)
// TODO: 読み取った値でProduct構造体を作成する
// TODO: productでInspectStructを呼び出し、結果を出力する
} else if structType == "employee" {
// employeeのフィールドを読み取る
idStr, _ := reader.ReadString('\n')
idStr = strings.TrimSpace(idStr)
id, _ := strconv.Atoi(idStr)
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
department, _ := reader.ReadString('\n')
department = strings.TrimSpace(department)
// TODO: 読み取った値でEmployee構造体を作成する
// TODO: employeeでInspectStructを呼び出し、結果を出力する
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー自分で練習してみよう: Goオンラインコンパイラ