Menu
Coddy logo textTech

リフレクションの基本

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

リフレクションを使用すると、プログラムは実行時に自身の構造を調べ、操作できます。Go の reflect パッケージはこの機能を提供し、型の検査、構造体のフィールドの読み取り、メソッドの動的な呼び出しを可能にします。

リフレクションにおける2つの基本的な型は、reflect.Typereflect.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: 30

Reflection は強力ですが、トレードオフも伴います。コンパイル時の型チェックを回避し、直接アクセスよりも遅く実行され、コードの理解を難しくします。JSON marshaling、ORM マッピング、またはコンパイル時に型がわからない汎用ユーティリティの構築などの作業に限定して使用してください。

challenge icon

チャレンジ

簡単

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 関数が、ProductEmployee の両方の構造体を動的に処理します。

自分で試してみよう

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を呼び出し、結果を出力する

	}
}
quiz icon腕試し

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

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

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