概要
Go 言語の仕様まとめ。
前回:
Go 言語仕様8(ポインタ)
内容
- 構造体
- 宣言・代入
- ポインタ
- メソッド
- 多重構造体
- スライスとの合せ技
構造体
- 宣言方法
構造体では、複数の任意の型を一つにまとめることができる。
宣言・代入
// 複数の任意の型を一つにまとめることができる
type Structure struct {
// 外部パッケージがら呼び出せるように、先頭一文字は大文字にしておく
Amount int
Language string
Decimal float64
Bl bool
}
func main() {
// 変数の宣言方法と同じ
var st Structure
st2 := Structure{}
// 各型の初期値が出力されることを確認
fmt.Println(st) // {0 0 false}
fmt.Println(st2) // {0 0 false}
// 値を代入(.で各フィールドにアクセスできる)
st.Amount = 1000
st.Language = "Golang"
fmt.Println(st.Amount, st.Language) // 1000 Golang
// 初期値を指定して宣言
st3 := Structure{
Amount: 2000,
Language: "Java",
Decimal: 1.234,
Bl: true,
}
fmt.Println(st3) // {2000 Java 1.234 true}
// 構造体のフィールドの順番で値のみを入れることも可能
st4 := Structure{3000, "Python", 3.14, false}
fmt.Println(st4)
// 全フィールド分代入する必要がある
st5 := Structure{3000, "Python", 3.14} // too few values in struct literal
}
- ポインタ
構造体は値型のため、関数へ引数で渡したときなどは、元の構造体の値に影響がない。
値渡し
type Structure struct {
Amount int
Language string
}
func Modify(st Structure) {
st.Amount = 9999
st.Language = "Golang"
}
func main() {
var st Structure
Modify(st)
fmt.Println(st) // {0 }
}
そのため、ポインタ変数を作り、関数へ参照渡しする。
参照渡し
type Structure struct {
Amount int
Language string
}
func Modify(st Structure) {
st.Amount = 9999
st.Language = "Golang"
}
func Modify2(st *Structure) {
st.Amount = 9999
st.Language = "Golang"
}
func main() {
var st Structure
Modify(st)
fmt.Println(st)
// 構造体のポインタを明示的に作る方法が推奨パターン
st2 := &Structure{}
Modify2(st2)
fmt.Println(*st2) // {9999 Golang}
// 非推奨パターンA
st3 := Structure{}
Modify2(&st3)
// 非推奨パターンB
st4 := new(Structure)
Modify2(st4)
}
- メソッド
メソッド:
構造体に紐付く処理群。紐付けた構造体でのみ使用可能。
関数:
構造体に紐付かない処理群。
レシーバ:
メソッドと紐付いた構造体。
関数を使った構造体の値更新
type Structure struct {
Amount int
Language string
}
func Modify(st *Structure) {
st.Amount = 2000
}
func main() {
// 関数を使った構造体の値更新
st := &Structure{100, "Golang"}
Modify(st)
fmt.Println(st) // &{2000 Golang}
}
メソッドを使った構造体の値更新
type Structure struct {
Amount int
Language string
}
// メソッドのレシーバーはポインタ型にするのが一般的
func (st *Structure) Modify() {
// レシーバーの値を書き換える
st.Amount = 3000
}
func main() {
st := &Structure{100, "Golang"}
// レシーバー.メソッドで呼び出し
st.Modify()
fmt.Println(st) // &{3000 Golang}
}
- 多重構造体
親子関係のような感じで、構造体を構造体のフィールドとして定義できる。
多重構造体
type SubStructure1 struct {
Amount int
Language string
}
type SubStructure2 struct {
Decimal float64
Bl bool
}
// 構造体をフィールドで持てる
type Structure struct {
SubStructure1 SubStructure1
// フィールド名が同じであれば、型宣言は省略可能
SubStructure2
}
// メソッド
func (subst *SubStructure1) Modify() {
subst.Amount = 9000
subst.Language = "Java"
}
func main() {
// 子の構造体にアクセスできる
st2 := Structure{}
fmt.Println(st2) // {{0 } {0 false}}
// 値を入れる
st2.SubStructure1.Amount = 100
st2.SubStructure1.Language = "Golang"
// 型宣言を省略した場合は、子の構造体に直接アクセス可能
st2.Decimal = 1.234
st2.Bl = true
fmt.Println(st2) // {{100 Golang} {1.234 true}}
// 値を入れつつ宣言
st3 := Structure{
SubStructure1: SubStructure1{
Amount: 8000,
Language: "Golang",
},
}
fmt.Println(st3) // {{8000 Golang} {0 false}}
// 親の構造体からこの構造体のメソッドを呼び出せる
st2.SubStructure1.Modify()
fmt.Println(st2)
}
- スライスとの合せ技
構造体をスライスで扱える。
type Structure struct {
Amount int
Language string
}
// 子の構造体をスライスでフィールドに持つ構造体
type SliceStruct struct {
Structure []*Structure
}
func main() {
// スライスで構造体を定義できる
sl := make([]Structure, 3)
fmt.Println(sl) // [{0 } {0 } {0 }]
// 各スライスに値を代入
sl[0].Amount = 100
sl[1].Language = "Golang"
sl[2].Amount = 200
fmt.Println(sl) // [{100 } {0 Golang} {200 }]
// スライスの構造体を定義して、
v := Structure{100, "Galang"}
v2 := Structure{100, "Java"}
v3 := Structure{100, "Python"}
// appendする
slst := SliceStruct{}
slst.Structure = append(slst.Structure, &v, &v2, &v3)
// forで値を取り出す
for _, v := range slst.Structure {
fmt.Println(v)
}
}