演習 - 構造体を使用する
フィールドのコレクションを 1 つの構造体で表す必要がある場合があります。 たとえば、給与支払いプログラムを記述する必要がある場合は、従業員のデータ構造を使用する必要があります。 Go では、構造体を使用して、レコードを形成できるさまざまなフィールドをグループ化できます。
Go の "構造体" は、任意の型の 0 個以上のフィールドを含み、それらを 1 つのエンティティとして表すことができる "もう 1 つのデータ型" です。
このセクションでは、構造体が不可欠である理由とその使用方法について説明します。
構造体を宣言して初期化する
構造体を宣言するには、struct
キーワードと、新しいデータ型で使用するフィールドとその型のリストを使用する必要があります。 たとえば、従業員の構造体を定義するには、次のコードを使用します。
type Employee struct {
ID int
FirstName string
LastName string
Address string
}
その後、他の型で通常行うときと同様に、次のように変数を新しい型で宣言できます。
var john Employee
変数の宣言と初期化を同時に行う場合は、次のようにします。
employee := Employee{1001, "John", "Doe", "Doe's Street"}
構造体の各フィールドに値を指定する必要があることに注意してください。 ただし、これは問題になる場合があります。 代わりに、構造体の初期化するフィールドを明示的に指定することができます。
employee := Employee{LastName: "Doe", FirstName: "John"}
前のステートメントから、各フィールドには任意の順序で値を割り当てることができることがわかります。 また、他のフィールドの値を指定しなくてもかまいません。 Go によって、フィールドのデータ型に応じて既定値が割り当てられます。
構造体の個々のフィールドにアクセスするには、次の例のようにドット表記 (.
) を使用します。
employee.ID = 1001
fmt.Println(employee.FirstName)
最後に、構造体へのポインターを生成するには、次のコードで示されているように &
演算子を使用します。
package main
import "fmt"
type Employee struct {
ID int
FirstName string
LastName string
Address string
}
func main() {
employee := Employee{LastName: "Doe", FirstName: "John"}
fmt.Println(employee)
employeeCopy := &employee
employeeCopy.FirstName = "David"
fmt.Println(employee)
}
上記のコードを実行すると、次の出力が表示されます。
{0 John Doe }
{0 David Doe }
ポインターを使用すると、構造体が変更可能になることに注目してください。
構造体の埋め込み
Go の構造体では、構造体に別の構造体を埋め込むことができます。 繰り返しを減らして共通の構造体を再利用したい場合があります。 たとえば、前のコードをリファクターして、Employee のデータ型と Contractor のデータ型を持つようにするとします。 次の例のように、共通のフィールドを保持する Person
構造体を作成できます。
type Person struct {
ID int
FirstName string
LastName string
Address string
}
その後、Employee
や Contractor
などの Person
型が埋め込まれた他の型を宣言できます。 別の構造体を埋め込むには、次の例のように新しいフィールドを作成します。
type Employee struct {
Information Person
ManagerID int
}
ただし、Person
構造体のフィールドを参照するには、次の例のように employee 変数の Information
フィールドを含める必要があります。
var employee Employee
employee.Information.FirstName = "John"
このようにコードをリファクタリングすると、コードが壊れてしまう可能性があります。 代わりに、次の例のように、埋め込む構造体と同じ名前の新しいフィールドを含めることができます。
type Employee struct {
Person
ManagerID int
}
デモとして次のコードを使用できます。
package main
import "fmt"
type Person struct {
ID int
FirstName string
LastName string
Address string
}
type Employee struct {
Person
ManagerID int
}
type Contractor struct {
Person
CompanyID int
}
func main() {
employee := Employee{
Person: Person{
FirstName: "John",
},
}
employee.LastName = "Doe"
fmt.Println(employee.FirstName)
}
すべてのフィールドが自動的に埋め込まれているため、Person
のフィールドを指定しなくても、Employee
構造体から FirstName
フィールドにアクセスできることに注目してください。 ただし、構造体を初期化する場合は、値を割り当てるフィールドを指定する必要があります。
JSON を使用して構造体をエンコードおよびデコードする
最後に、構造体を使用して、JSON のデータをエンコードおよびデコードできます。 Go には JSON 形式に対する優れたサポートがあり、標準ライブラリ パッケージに既に含まれています。
また、構造体のフィールドの名前を変更したりすることもできます。 たとえば、JSON の出力で FirstName
と表示せずに単に name
と表示したり、空のフィールドを無視したりするとします。 次の例に示すように、フィールドのタグを使用できます。
type Person struct {
ID int
FirstName string `json:"name"`
LastName string
Address string `json:"address,omitempty"`
}
その後、構造体を JSON にエンコードするには、json.Marshal
関数を使用します。 JSON 文字列をデータ構造にデコードするには、json.Unmarshal
関数を使用します。 すべてをまとめて、従業員の配列を JSON にエンコードし、その出力を新しい変数にデコードする例を次に示します。
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
ID int
FirstName string `json:"name"`
LastName string
Address string `json:"address,omitempty"`
}
type Employee struct {
Person
ManagerID int
}
type Contractor struct {
Person
CompanyID int
}
func main() {
employees := []Employee{
Employee{
Person: Person{
LastName: "Doe", FirstName: "John",
},
},
Employee{
Person: Person{
LastName: "Campbell", FirstName: "David",
},
},
}
data, _ := json.Marshal(employees)
fmt.Printf("%s\n", data)
var decoded []Employee
json.Unmarshal(data, &decoded)
fmt.Printf("%v", decoded)
}
上記のコードを実行すると、次の出力が表示されます。
[{"ID":0,"name":"John","LastName":"Doe","ManagerID":0},{"ID":0,"name":"David","LastName":"Campbell","ManagerID":0}]
[{{0 John Doe } 0} {{0 David Campbell } 0}]