構造体
構造体は、フィールド(field)の集まりです。
定義方法
構造体は下記のように type
と struct
を使用して定義します。
-
構文
type 構造体の名前 struct { フィールド名 型名 フィールド名 型名 フィールド名 型名 ... }
同じ型のフィールド名は、カンマ区切りで書くことができます。
type 構造体の名前 struct { フィールド名, フィールド名, フィールド名 型名 フィールド名 型名 }
-
例
人のデータをまとめた構造体です。
type People struct { Name string Sex string Age int Height int Weight int }
初期化
方法1:Key:Valueで初期化します。
people := People{Name: "太郎"}
設定しない要素は ゼロ値 で初期化される。
方法2:要素の定義順に初期化します。
people := People{"太郎", "男", ...}
この方法では、全部のfieldを設定する必要があります。
方法3:定義後にドット(.)を用いてfieldを初期化します。
people := People{}
people.Sex = "男"
people3.Age = 8
構造体へのポインター(Pointer to structs)
方法1:&
(アドレス演算子)で構造体へのPointerを取得します。
people := People{Name: "太郎"}
p := &people
(*p).Name = "花子"
(*p)
は p
に省略できます。
p.Name = "花子"
方法2: new(構造体名)
で構造体へのポインターを生成します。
people := new(People) // Peopleのポインタ型
(*people).Name = "花子"
people.Name = "花子" // (*people) は peopleに省略できます。
new(T)
はT型の無名変数(unnamed variable)を作成し、それをT
のゼロ値へ初期化し、*T
型の値であるそのアドレスを返します。
式 new(People)
と &People{}
は等価です。
タグ(Tag)
Go構造体タグは、Go構造体宣言のフィールドの後に表示される注釈です。
実行時にreflect
パッケージでその情報を取得し利用したりします。
良くある構造体をJSONに変換する例として、
下記のようにフィールドの後ろに json
タグをつけると、
type People struct {
Name string `json:"name"`
Sex string `json:"sex"`
Age int `json:"age"`
Height int `json:"height"`
Weight int `json:"weight"`
}
Go言語の json
標準パッケージで構造体をJSONに変換すると、フィールド名がTagで定義された名前に置換されます。
people := People{Name: "太郎", Sex: "男"}
jsonBytes, _ := json.Marshal(people)
fmt.Println(string(jsonBytes))
// JSON: {"name": "太郎", "sex": "男"}
その仕組み
json
パッケージの内部で下記のようにタグ情報を取得してJSONデータに変換しています。
t := reflect.TypeOf(people)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
tag := field.Tag.Get("json") // `json`タグ情報を取得
}
下記のように、複数のタグはスペースで区切って記述できます。
type People struct {
Name string `json:"name" validate:"required"`
Sex string `json:"sex" validate:"required"`
Age int `json:"age" validate:"required"`
Height int `json:"height" validate:"required"`
Weight int `json:"weight" validate:"required"`
}
埋め込みフィールド(Embedded fields)
型だけで宣言されていて、明示的なフィールド名がないフィールドを埋め込みフィールドと呼びます。
Go言語は埋め込みで他言語のクラスの継承ぽいことができます。
埋め込み子構造体(Address
)のフィールドが昇格し、構造体の外(Person
)からダイレクトにアクセスすることが可能です。
person.Adress.city → person.city
person.Adress.state → person.state
// PersonがAdressを継承しているように、Adressのフィールドをダイレクトにアクセスすることが可能となります。
Person
に同名のフィールドやメソッドがあるときは、Address
にあるそのフィールドは昇格されないです。
構造体の比較
reflect.DeepEqual
はポインターか否かにかかわらず、
フィールドの値が同じであれば true
を返します。
import (
"reflect"
)
...
people1 := People{Name: "太郎"}
people2 := People{Name: "太郎"}
println(reflect.DeepEqual(people1, people2)) // true
println(reflect.DeepEqual(&people1, &people2)) // true
println(people1 == people2) // true
println(&people1 == &people2) // false
動画(YouTube)
元記事