目次

前書き

この本の構成
動作確認環境
免責事項

第1章 reflectの基礎知識

1.1 reflectとは
1.2 reflectのオブジェクトとは
1.3 reflectパッケージの利用例

第2章 reflectの基本的な使い方

2.1 型名を取得する
2.2 型の種別を取得する
2.3 型の種別ごとの処理を行う
2.4 型同士の比較
2.5 値同士の比較
2.6 元の値を取得する
2.7 元の値を更新できるか確認する
2.8 元の値を更新する
2.9 ゼロ値であるかを調べる
2.10 ゼロ値を取得する
2.11 配列のすべての要素を処理する
2.12 マップのすべての要素を処理する
2.13 構造体のフィールドを取得する
2.14 構造体のフィールドのタグを取得する

第3章 reflectの使用例・Type編

3.1 type Type
3.2 func Align
3.3 func FieldAlign
3.4 func Size
3.5 func Method
3.6 func MethodByName
3.7 func NumMethod
3.8 func Name
3.9 func String
3.10 func PkgPath
3.11 func Kind
3.12 func Implements
3.13 func AssignableTo
3.14 func ConvertibleTo
3.15 func Comparable
3.16 func Bits
3.17 func Elem
3.18 func ChanDir
3.19 func Key
3.20 func Field
3.21 func FieldByIndex
3.22 func FieldByName
3.23 func FieldByNameFunc
3.24 func NumField
3.25 func Len
3.26 func In
3.27 func Out
3.28 func NumIn
3.29 func NumOut
3.30 func IsVariadic

第4章 reflectの使用例・Value編

4.1 type Value
4.2 func (Value) Int
4.3 func (Value) Uint
4.4 func (Value) Float
4.5 func (Value) Complex
4.6 func (Value) String
4.7 func (Value) Bool
4.8 func (Value) Bytes
4.9 func (Value) Interface
4.10 func (Value) CanInterface
4.11 func (Value) Addr
4.12 func (Value) CanAddr
4.13 func (Value) Call
4.14 func (Value) CallSlice
4.15 func CanSet
4.16 func Cap
4.17 func Close
4.18 func Convert
4.19 func Elem
4.20 func NumField
4.21 func Field
4.22 func FieldByIndex
4.23 func FieldByName
4.24 func FieldByNameFunc
4.25 func Index
4.26 func InterfaceData
4.27 func IsNil
4.28 func IsValid
4.29 func IsZero
4.30 func Kind
4.31 func Len
4.32 func MapIndex
4.33 func MapKeys
4.34 func MapRange
4.35 func Method
4.36 func MethodByName
4.37 func NumMethod
4.38 func OverflowInt
4.39 func OverflowUint
4.40 func OverflowFloat
4.41 func OverflowComplex
4.42 func Pointer
4.43 func Recv
4.44 func Send
4.45 func Set
4.46 func SetBool
4.47 func SetBytes
4.48 func SetCap
4.49 func SetComplex
4.50 func SetFloat
4.51 func SetInt
4.52 func SetUint
4.53 func SetString
4.54 func SetLen
4.55 func SetMapIndex
4.56 func SetPointer
4.57 func Slice
4.58 func Slice3
4.59 func TryRecv
4.60 func TrySend
4.61 func Type
4.62 func UnsafeAddr

第5章 reflectの使用例・その他編

5.1 func Copy
5.2 func DeepEqual
5.3 func Swapper
5.4 type ChanDir
5.5 type Kind
5.6 type MapIter
5.7 type Method
5.8 func Select
5.9 type SelectCase
5.10 type SelectDir
5.11 type SliceHeader
5.12 type StringHeader
5.13 type StructField
5.14 type StructTag
5.15 func ArrayOf
5.16 func ChanOf
5.17 func FuncOf
5.18 func MapOf
5.19 func PtrTo
5.20 func SliceOf
5.21 func StructOf
5.22 func TypeOf
5.23 func Append
5.24 func AppendSlice
5.25 func Indirect
5.26 func MakeChan
5.27 func MakeFunc
5.28 func MakeMap
5.29 func MakeMapWithSize
5.30 func MakeSlice
5.31 func New
5.32 func NewAt
5.33 func ValueOf
5.34 func Zero

付録A reflectの型ごとの使用例

A.1 符号付き整数
A.2 符号なし整数
A.3 浮動小数点数
A.4 複素数
A.5 真偽値
A.6 文字列
A.7 配列
A.8 スライス
A.9 マップ
A.10 構造体
A.11 関数
A.12 ポインタ
A.13 インターフェイス
A.14 チャネル

前書き

 この本では、Go言語のreflectパッケージについて、解説と使い方の紹介をします。

 reflectは、実行時リフレクションを実装するパッケージです。任意の値の型情報などをオブジェクト化して利用する機能を持っています。これを使用することで、あたかも動的型付け言語であるかのように、型の制約を受けずにコードが書けます。

 reflectはこのように言語の根底を覆すような強力なパッケージではありますが、強力であるがゆえに、使い方を間違えると簡単に危険なコードが書けます。取り扱いには注意が必要です。

 この本がreflectを使うための、あるいは不適当な場面で「使わない」と適切に判断するための助けになりますと幸いです。

この本の構成

 第1章ではreflectパッケージとはなにか、どのような仕組みで動いているのか、どのように使われているのかといった基礎知識を紹介します。

 第2章では、reflectパッケージの基本的な使用例を目的別に紹介します。

 第3章では、reflectパッケージのType型のすべてのメソッドの使用例を紹介します。この章と第4章と第5章では、関数やメソッドとその使用例を羅列しています。必要になったタイミングで、辞書的にご参照ください。

 第4章では、reflectパッケージのValue型のすべてのメソッドの使用例を紹介します。

 第5章では、reflectパッケージのType型とValue型以外の型や、関数やメソッドの使用例を紹介します。

 付録Aでは、reflectパッケージの型ごとの使用例を紹介します。reflectのオブジェクトは型によって利用できるメソッドが異なるので、型ごとに何ができるのかがわかりやすいようにまとめました。

動作確認環境

 この本のプログラムは、Goのバージョン1.14で動作確認をしました。

免責事項

 本書に記載された内容は、情報の提供のみを目的としています。したがって、本書を用いた開発、製作、運用は、必ずご自身の責任と判断によって行ってください。これらの情報による開発、製作、運用の結果について、著者はいかなる責任も負いません。

第1章 reflectの基礎知識

 この章では、reflectパッケージとはなんなのか、どんな仕組みで動いているのかを紹介します。

1.1 reflectとは

 reflectはリフレクションを実装したパッケージです。

 リフレクションとは、プログラムの実行過程でプログラム自身の構造を読み取ったり、書き換えたりする技術のことです。

 reflectパッケージを使うと、値の処理系内部での情報を反映したオブジェクトを取得できます。値の処理系内部での情報とはたとえば、どんな型であるかとか、メモリー上で何バイト割り当てられているかなどです。

1.2 reflectのオブジェクトとは

 reflectの主なオブジェクトはふたつあります。

 ひとつ目は処理系内部での型についての情報が反映されたオブジェクトで、型名はreflect.Typeです。ふたつ目は処理系内部での値についての情報が反映されたオブジェクトで、型名はreflect.Valueです。

1.2.1 reflect.Typeとは

 reflect.Typeは、処理系内部での型についての情報が反映されたオブジェクトです。型の名前や型の種別、パッケージのパスやメモリー上の割当バイト数などの情報をプロパティーとして持っています。

reflect.Typeの取得方法

 任意の値のreflect.Typeを取得するには、reflect.TypeOf関数を使います。reflect.TypeOfのシグネチャはこちらです。

func TypeOf(i interface{}) Type

 引数iはinterface{}型の値であり、これは空のインターフェイスです。空のインターフェイスは実装するべきメソッドを持たないので、どんな型の値でも空のインターフェイスを実装しているとみなされ、どんな型の値でも代入できます。

 引数iは情報の反映元の値であり、この値のことを本書では便宜的に「反映元の値」あるいは「元の値」と呼びます。

reflect.Typeの主な使用方法

 reflect.Typeの主な使い方は、実行時に型を判定して、型ごとに異なる処理を行うというものです。

 下記のコードのisInt関数は、reflect.Typeを使って引数の型を判定し、引数が整数の場合にtrueを返します。

 型の判別には、reflect.TypeのKindメソッドで取得できるreflect.Kind型の値を利用します。reflectパッケージには、整数や文字列などに対応するreflect.Kind型の定数が定義されており、引数のKindを定数のKindと比較して型を判別します。

 reflect.Intなどは、符号付き整数型に対応するreflect.Kind定数です。

package main

  import (
          "fmt"
          "reflect"
  )

  func isInt(v interface{}) bool {
          rt := reflect.TypeOf(v)
          switch rt.Kind() {
          case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
                  return true
          default:
                  return false
          }
  }

  func main() {
          fmt.Println(isInt(1))     //=> true
          fmt.Println(isInt("1"))   //=> false
          fmt.Println(isInt(false)) //=> false
  }

1.2.2 reflect.Valueとは

 reflect.Valueは、処理系内部での値についての情報が反映されたオブジェクトであり、値の各種操作や元の値の取得、値の更新などが行えます。

 値の各種操作とはたとえば、元の値がスライスのreflect.Valueに対してappendを行ったり、元の値がチャネルのreflect.Valueに送受信を行ったりすることです。

 また、Typeメソッドでreflect.Typeを、Kindメソッドでreflect.Kindを取得できるので、reflect.Valueは値だけではなく、型についての情報も反映されたオブジェクトであるといえます。

reflect.Valueの取得方法

 任意の値のreflect.Valueを取得するには、reflect.ValueOf関数を使います。reflect.ValueOfのシグネチャはこちらです。

func ValueOf(i interface{}) Value

 引数iはreflect.TypeOfの場合と同様に、どんな型の値でも代入できる空のインターフェイスです。

 引数iは情報の反映元の値であり、この値のことを本書では便宜的に「反映元の値」あるいは「元の値」と呼びます。

reflect.Valueの主な使用方法

 reflect.Valueの主な使い方は、実行時に型や値を判定して値を利用した処理を行うというものです。

 下記のコードのplus関数はreflect.Valueを使って引数の型を判定し、引数が整数の場合に加算を行い、引数が文字列の場合に文字列結合を行います。

 reflect.Valueの取得にはreflect.ValueOf関数を用い、型の判定にはKindメソッドを利用しています。

 元の値の取得は、IntメソッドやStringメソッドなど元の値の方に合わせて、適切なメソッドを利用します。

package main

  import (
          "fmt"
          "reflect"
  )

  func plus(v1, v2 interface{}) interface{} {
          rv1, rv2 := reflect.ValueOf(v1), reflect.ValueOf(v2)
          if isInt(rv1.Kind()) && isInt(rv2.Kind()) {
                  return interface{}(rv1.Int() + rv2.Int())
          } else if rv1.Kind() == reflect.String && rv2.Kind() == reflect.String {
                  return interface{}(rv1.String() + rv2.String())
          }
          return nil
  }

  func isInt(k reflect.Kind) bool {
          return k == reflect.Int || k == reflect.Int8 || k == reflect.Int16 || k == reflect.Int32 || k == reflect.Int64
  }

  func main() {
          fmt.Println(plus(100, 200))     //=> 300
          fmt.Println(plus("100", "200")) //=> 100200
          fmt.Println(plus(100, "200"))   //=> nil
  }

1.3 reflectパッケージの利用例

 reflectパッケージがどのように役に立つのかを知るために、標準パッケージでの利用例を紹介します。

1.3.1 fmtパッケージでの利用例

 fmtパッケージは内部でreflectを多用しています。わかりやすい利用例として、型名や構造体のフィールド名などを表示する際には、内部でreflect.Typeなどが使われています。

fmtでの利用例

package main

  import "fmt"

  type Animal struct {
          Name string
          Age  int
  }

  func main() {
      v := Animal{
          Name: "dog",
          Age:  3,
      }

      // 型名の表示
      fmt.Printf("%T", v) //=> main.Animal

      // フィールド名と値の表示
      fmt.Printf("%+v", v) //=> {Name:dog Age:3}
  }

 fmt.Printfでは値をinterface{}型として受け取り、内部でreflect.Typeなどに変換し型情報を取得し、型名の表示や型に合わせた処理の分岐などを行っています。

1.3.2 encoding/jsonパッケージでの利用例

 encoding/jsonパッケージでは、reflectを使って構造体のフィールドのタグに記載された設定情報を読み取り、JSONのエンコードとデコードの際に利用しています。

encoding/jsonでの利用例

package main

  import (
          "encoding/json"
          "fmt"
          "log"
  )

  type Animal struct {
          Name string `json:"name"`
          Age  int    `json:"age"`
  }

  func main() {
          data := []byte(`{"name":"cat", "age":5}`)
          v := Animal{}
          if err := json.Unmarshal(data, &v); err != nil {
                  log.Fatal(err)
          }
          fmt.Println(v.Name, v.Age) //=> cat 5
  }

第2章 reflectの基本的な使い方

 この章では、reflectパッケージの基本的な使い方をユースケースごとに紹介します。個々の関数やメソッドごとの使用例は、第3章、第4章、第5章をご覧ください。

2.1 型名を取得する

 reflect.Typeから型名を取得するためには、NameメソッドかStringメソッドを使います。

 Nameメソッドはintやstringなどの基本型や、type文で定義された型の名前を返します。スライスやマップ、構造体やポインタなどの複合型の場合は、空文字列を返します。

 Stringメソッドはすべての型の名前を返します。type文で定義された型の場合は、パッケージ名もつきます。このときのパッケージ名は省略形となり、たとえばmath/randならrandとなります。

型名の取得

package main

  import (
          "fmt"
          "math/rand"
          "reflect"
  )

  func main() {
          // 整数
          rt := reflect.TypeOf(100)
          fmt.Println(rt.Name())   //=> int
          fmt.Println(rt.String()) //=> int

          // スライス
          rt = reflect.TypeOf([]int{})
          fmt.Println(rt.Name())   //=>
          fmt.Println(rt.String()) //=> []int

          // 構造体
          type T1 struct {
                  F1 int
                  F2 string
          }
          rt = reflect.TypeOf(T1{})
          fmt.Println(rt.Name())   //=> T1
          fmt.Println(rt.String()) //=> main.T1

          // 構造体リテラル
          rt = reflect.TypeOf(struct {
                  F1 int
                  F2 string
          }{})
          fmt.Println(rt.Name())   //=>
          fmt.Println(rt.String()) //=> struct { F1 int; F2 string }

          // 構造体(パッケージ名に階層がある場合)
          rt = reflect.TypeOf(rand.New(rand.NewSource(99)))
          fmt.Println(rt.Name())    //=>
          fmt.Println(rt.String())  //=> *rand.Rand
  }

2.2 型の種別を取得する

 reflect.Typeから型の種別を取得するためには、Kindメソッドを使います。

 Kindメソッドはreflect.Kind型の値を返します。

 reflectパッケージでは、整数のKindや文字列のKindなどが定数として定義されています。そのため、Typeから取得したKindを定数と比較して型の種別を判別し、型ごとの処理を実装します。

Kindの取得

package main

  import (
          "fmt"
          "reflect"
          "unsafe"
  )

  func main() {
          fmt.Println(reflect.TypeOf(0).Kind() == reflect.Int) //=> true

          fmt.Println(reflect.TypeOf(uint(0)).Kind() == reflect.Uint) //=> true

          fmt.Println(reflect.TypeOf(float64(0)).Kind() == reflect.Float64) //=> true

          fmt.Println(reflect.TypeOf(false).Kind() == reflect.Bool) //=> true

          fmt.Println(reflect.TypeOf("").Kind() == reflect.String) //=> true

          fmt.Println(reflect.TypeOf([1]int{}).Kind() == reflect.Array) //=> true

          fmt.Println(reflect.TypeOf([]int{}).Kind() == reflect.Slice) //=> true

          fmt.Println(reflect.TypeOf(map[int]bool{}).Kind() == reflect.Map) //=> true

          fmt.Println(reflect.TypeOf(make(chan int)).Kind() == reflect.Chan) //=> true

          fmt.Println(reflect.TypeOf(func() {}).Kind() == reflect.Func) //=> true

          type S struct{}
          fmt.Println(reflect.TypeOf(S{}).Kind() == reflect.Struct) //=> true

          fmt.Println(reflect.TypeOf(&S{}).Kind() == reflect.Ptr) //=> true
  }
試し読みはここまでです。
この続きは、製品版でお楽しみください。