Go 言語の構造体 (struct)
Go には明示的なクラス定義がありません。オブジェクト志向プログラミングの実装には、ここで説明する構造体を利用します。
Go の構造体とは?
複数のデータをひとまとまりにして扱いたい場合に、構造体 (struct) を利用すると便利です。
例えば、人 (person) のデータモデルを考えた時に、「名前 (name)」と「年齢 (age)」をひとまとまりにして、特定のひとの情報としたいとします。
このとき、Go では次のように書くことで、 string 型の name と int 型の age という二つのデータフィールドをもつ、 person という名前のデータ構造を定義することができます。
type person struct {
name string
age int
}
type キーワードで構造体をひとつ作るということは、struct で定義された新しい型 (type) を定義する、ということになります。
これを使うと、次のように person 型の変数を使うことができます。
var p person
p.name = "John"
p.age = 30
それでは、構造体の使い方をもう少しみていきましょう。
Go の構造体の定義方法
Go の構造体は struct キーワードを使って、次のように定義できます。
type 構造体の名前 struct {
フィールド名1 データ型1
フィールド名2 データ型2
...
}
フィールドは任意の数だけ定義できます。
構造体の名前はアルファベットの文字で始めます。外部パッケージからのアクセスを許可する場合は、大文字で始めます。
また、フィールド名も外部パッケージからのアクセスを許可する場合は、大文字のアルファベットから始まるように命名します。
Go の構造体の作成
上で定義した person 型をもう一度見てみましょう。
type person struct {
name string
age int
}
この構造体は、ビルトインの型と同様に次のように作成できます。
var p person
:= を使う場合は構造体のリテラル {} を使って次のように書けます。
p := person{}
構造体の作成時に初期化を行う場合には、次のように書けます。
p := person{
name: "John",
age: 30,
}
フィールド名は省略でき、次のように書いても同じです。
p := person{
"John",
30,
}
とはいえ、フィールド名がないと何に何の値をセットしているかわかりにくいので、多用しない方が無難と思います。
Go の構造体にメソッドを実装する
特定の型が関連付けされた関数のことを特に、メソッド (method) といいます。
Go の関数の定義にレシーバ (receiver)を指定することによって、レシーバで指定した型と関数を関連付けできます。
func レシーバ メソッド名(引数リスト) 戻り値リスト {
}
レシーバは (変数 型) という形式で記述します。
具体例をみてみましょう。上述の person 型の構造体に hello() というメソッドを定義するには次のように書きます。
package main
import "fmt"
type person struct {
name string
age int
}
func (p person) hello() {
fmt.Printf("%s (%d)\n", p.name, p.age)
}
func main() {
p := person{
name: "John",
age: 30,
}
p.hello() // John (30)
}
10行目、func キーワードの後に書いてある (p person) の部分がレシーバになります。 このレシーバを経由して構造体のフィールドの値にアクセスできます。
Go のポインタレシーバとバリューレシーバ
関数の引数に参照渡しと値渡しがあるのと同様に、レシーバにも参照渡しと値渡しがあります。
参照渡しのレシーバを特に ポインタレシーバ (pointer receiver)、値渡しのレシーバーをバリューレシーバ(value receiver) といいます。
メソッド内でフィールドの値を変更する場合には、ポインタレシーバを利用する必要があります。
Go のポインタについては、「Go 言語のポインタ」を参考にしてください。
具体例として、上記 person 型に、年齢 (age) をひとつインクリメントする increment() メソッドを追加してみましょう。
func (p person) hello() {
fmt.Printf("%s (%d)\n", p.name, p.age)
}
func (p *person) increment() {
p.age++
}
func main() {
p := person{
name: "John",
age: 30,
}
p.hello() // John (30)
p.increment()
p.hello() // John (31)
}
5行目のレシーバが (p *person) というように、ポインターで渡されているところに注意してください。
6行目で age をインクリメントしています。ポインタレシーバーであるために、このインクリメントは成功します。このため、15行目で increment() を呼び出した後に、 もう一度 hello() を呼び出すと、 age がひとつ増えて 31 となっていることがわかります。
以上、ここでは Go 言語の構造体の基本について説明しました。