3.3 構造体にメソッドを定義する

Goではどのような型に対してもメソッドを追加できます。そのため、構造体も型定義すればメソッドが設定できます。Goはオブジェクト指向言語ではない、とはいうものの、「関数とほぼ同じだがレシーバーを持つ」という特徴は他のオブジェクト指向言語のメソッドとほぼ同一ですし、メソッドを設定された構造体は他のオブジェクト指向言語のクラスにかなり近い存在です。大きなアプリケーションを構成するにあたって、「メソッドを持った構造体」は重要な構成部品となります。本節では、2章のメソッド定義に加えて、構造体を基底型とする型にメソッドを定義するときに課題になることを追加で説明していきます。

メソッドの定義方法は第2章で紹介した通りで、通常のメソッドにレシーバーを(レシーバー変数名 型名)形式で追加したものです。レシーバーの変数名には1文字程度のシンプルな変数名を用いて、selfthisは使わない、と紹介しました。他の言語のメソッドに似ていると紹介しましたが、内部実装も似ていて、レシーバーはゼロ番目の引数として関数に渡されます。

// メソッドを追加する型
// 構造体でなくてもプリミティブ型に対する型も可能
type Struct struct {
	v int
}

// レシーバーを持つ関数としてメソッドを定義
func (s Struct) PrintStatus() {
	log.Println("Struct:", s.v)
}

レシーバーに指定できる型には、値型(Value Receiver)またはポインター型(Pointer Receiver)があります。

3.3.1 値レシーバーとポインターレシーバーのどちらを使えば良いか

レシーバーの型は値型か、ポインター型にします。値型にすると、レシーバーの属性を書き換えても変更は保存されません。ポインターにすれば変更が保存されます。注意点として、値型を指定したレシーバーのフィールド変数を書き換えてもエラーにはなりません。メソッドを呼んでも結果が変更されずおかしい、というバグの原因はたいていこれです。 ...