第2部 Go コレクションでは、複数の値を 1 つにまとめて管理できる配列、スライス、マップを紹介しました。しかしこれらのコレクションでは、複数の値を管理できても、違う型の値は管理できません。
Go では、複数の違う型の値を管理するためには構造体というものを使います。
構造体は、参照型としても、値型としても使えます。
Go には、オブジェクト指向のクラスというものがありません。
Go では、構造体に関数を関連づけることによって、クラスのような働きをさせます。
package main
import "fmt"
type student struct {
name string
height int
weight float64
score map[string]int
}
func main() {
println("---Empty struct---")
s := new(student)
fmt.Println(s.name) // fmt.Println((*s1).name) とも記述できます
fmt.Println(s.height)
fmt.Println(s.weight)
fmt.Println(s.score)
fmt.Println( s) // 値の前に & が付く
fmt.Println(*s) // 値の前に & は付かない
println(s)
println("----Initialize----")
s.name = "Jane"
s.height = 168
s.weight = 55.5
s.score = map [string] int {"math":100, "science":80, "english":60}
fmt.Println(s.name)
fmt.Println(s.height)
fmt.Println(s.weight)
fmt.Println(s.score)
fmt.Println(*s)
println(s)
}
---Empty struct---
0
0
map[]
&{ 0 0 map[]}
{ 0 0 map[]}
0xc000082000
----Initialize----
Jane
168
55.5
map[english:60 math:100 science:80]
{Jane 168 55.5 map[english:60 math:100 science:80]}
0xc000082000
type student struct {
name string
height int
weight float64
score map[string]int
}
構造体は、main 関数の外側、パッケージのルートで定義します。
構造体の定義は、type キーワード、任意の構造体名、struct キーワードの次に { と }(波括弧)で囲んで、個々のデータの変数名と型を記述します。
Go では、構造体の個々のデータのことを、フィールドと呼びます。
s := new(student)
構造体を参照型として宣言(作成)するには new 関数を使います。new 関数で宣言された構造体の各フィールドには、string 型であれば空文字列、数値型であれば 0 が初期値として設定されます。
fmt.Println(s.name) // fmt.Println((*s1).name) とも記述できます
各フィールドの値を取得するには、変数名 .(ドット)フィールド名と記述します。(*変数名).フィールド名とも記述できます。文字列型のフィールドの初期値は空文字列ですので、ターミナルには空行が表示されます。
fmt.Println( s) // 値の前に & が付く
fmt.Println(*s) // 値の前に & は付かない
構造体の全体の値を表示する際には、変数名の前に *(アスタリスク)を付けなければ、&(アンパサンド)が値の前に表示されます。
s.name = "Jane"
フィールドの値を変更するには、変数名 . フィールド名に値を代入します。
println(s)
構造体のアドレスは、宣言直後も、編集後も同じです。
package main
import "fmt"
type student struct {
name string
height int
weight float64
score map[string]int
}
func main() {
println("------ Empty struct ------")
s1 := student{} // newを使わずに構造体を宣言した場合には、その変数は値型になります
fmt.Println(s1) // 値型の構造体なので * を付けなくても & は表示されません
//println( s1) println 関数は、値型の構造体の値は表示できません
println(&s1) //println 関数は、値型の構造体のアドレスは表示できます
println("--- Initialized struct ---")
s2 := student{"Kate", 172, 61.5, map [string] int {"math":65, "science":75, "english":95}}
fmt.Println(s2.name) // 値型の場合も変数名 . フィールド名でフィールドの値を取得できます
fmt.Println(s2.height)
fmt.Println(s2.weight)
fmt.Println(s2.score)
fmt.Println(s2)
println(&s2)
println("------ Access Field ------")
s1.name = "Mary"
s1.height = 170
s1.weight = 58.5
s1.score = map[string]int{"math":85, "science":80, "english":90}
fmt.Println(s1.name) // 値型の場合も変数名 . フィールド名でフィールドの値を設定できます
fmt.Println(s1.height)
fmt.Println(s1.weight)
fmt.Println(s1.score)
println(&s1) // フィールドの値の変更後も変数のアドレスは変わりません
}
------ Empty struct ------
{ 0 0 map[]}
0xc00006ef38
--- Initialized struct ---
Kate
172
61.5
map[english:95 math:65 science:75]
{Kate 172 61.5 map[english:95 math:65 science:75]}
0xc00006ef10
------ Access Field ------
Mary
170
58.5
map[english:90 math:85 science:80]
0xc00006ef38
今回は、コードの説明を、サンプルコードのコメントに書きました。
そのほかに注意すべき点は、フィールドにアクセスする場合、参照型でも値型でも、変数名 . フィールド名でアクセスするという点です。
現在では、多くの言語で、参照型でも値型でも .(ドット)でアクセスするようになっています。しかし、C や C++ では、値型には .(ドット)でアクセスしますが、参照型のフィールドには ->(アロー演算子)でアクセスします。