メソッド

メソッドは、型(レシーバ)に対する振る舞いを宣言するものです。

メソッドの書き方

メソッドは関数と似ていますが、メソッドにはレシーバという特別な引数がある点で異なります。

レシーバは関数名の前に(変数名 型)の形式で宣言します。

レシーバとして宣言できるのは、typeで宣言された型とそのポインタ型です。

// 長方形
type Rectangle struct {
    Width  int
    Height int
}

// 長方形の面積を求める
// (レシーバに対する振る舞いを宣言)
func (r Rectangle) Area() int {
    return r.Width * r.Height
}

func main() {
    r := Rectangle{2, 3}
    // 変数名.メソッド名() の形式でメソッドを呼び出す
    fmt.Println(r.Area()) // 6
}

値レシーバとポインタレシーバ

メソッドのレシーバは、次の2種類があります。

  • 任意の型そのものをレシーバとする『値レシーバ』((t T)
  • 任意の型のポインタをレシーバとする『ポインタレシーバ』((t *T)

レシーバの値を変更するには、ポインタレシーバを使います。 メソッド呼び出しでは、ポインタ生成の&は省略できます。

同じ型のメソッド同士は、値レシーバとポインタレシーバを混在させず、どちらかに統一すべきです。

type Counter int

// 値レシーバ
func (c Counter) MinusOne() {
    c--
}

// ポインタレシーバ
func (c *Counter) PlusOne() {
    *c++
}

func main() {
    c := Counter(0)
    fmt.Println(c) // 0
    c.MinusOne()   // cの変更は反映されない
    fmt.Println(c) // 0
    (&c).PlusOne() // cの変更は反映される
    fmt.Println(c) // 1
    c.PlusOne()    // cのポインタ生成の記述(&)は省略できる
    fmt.Println(c) // 2
}

コンストラクタ

他のオブジェクト指向プログラミング言語では、任意の型を初期化するため仕組みとしてコンストラクタがあります。 Goではコンストラクタ用の構文はありませんが、通常の関数を使うことでコンストラクタを実現できます。 慣例として関数名はNew型名とします。

type Rectangle struct {
    Width  int
    Height int
}

// Rectangleのコンストラクタ
func NewRectangle(width int, height int) *Rectangle {
    return &Rectangle{Width: width, Height: height}
}

func main() {
    r := NewRectangle(2, 3)
    fmt.Println(r) // &{2 3}
}