コンテンツにスキップ

Go/メソッド

出典: フリー教科書『ウィキブックス(Wikibooks)』
< Go

Goにおけるメソッド(method)とは、特定のに関連付けられた関数です。通常の関数とは異なり、メソッドはレシーバ(receiver)と呼ばれる特別な引数を持ちます。このレシーバは、メソッドが呼び出される対象となる型の値です。

メソッドを使用することで、特定の型の振る舞いを定義し、その型のインスタンスに対して操作を行うことができます。オブジェクト指向プログラミングにおける「メソッド」の概念と類似しています。

メソッドの定義

[編集]

Goでメソッドを定義する基本的な構文は以下の通りです。

func (r ReceiverType) MethodName(parameter1 Type1, parameter2 Type2, ...) (returnType1, returnType2, ...) {
    // メソッドの処理
    // レシーバ r を通して、ReceiverType の値にアクセスできる
}
  • func: 関数の定義を示すキーワード
  • (r ReceiverType): レシーバの定義。
    • r: レシーバの名前(慣例的に、型の名前の最初の文字の小文字が使われることが多いです)。メソッド内でレシーバの値にアクセスするために使用します。
    • ReceiverType: メソッドが関連付けられる型。これは、構造体、組み込み型(int, string など)、またはユーザー定義の型(type MyInt int など)を指定できます。
  • MethodName: メソッドの名前
  • (parameter1 Type1, parameter2 Type2, ...): メソッドの引数リスト(通常の関数と同様)
  • (returnType1, returnType2, ...): メソッドの戻り値の型リスト(通常の関数と同様)
  • { ... }: メソッドの処理を記述するコードブロック

レシーバの種類

[編集]

レシーバには、以下の2つの種類があります。

  1. 値レシーバ (Value Receiver):
    • レシーバの型が直接指定されます(例: (s MyStruct))。
    • メソッド内でレシーバの値がコピーとして扱われます。
    • メソッド内でレシーバのフィールドを変更しても、メソッド呼び出し元の値には影響を与えません。
  2. ポインタレシーバ (Pointer Receiver):
    • レシーバの型がポインタとして指定されます(例: (s *MyStruct))。
    • メソッド内でレシーバの値へのポインタが渡されます。
    • メソッド内でレシーバのフィールドを変更すると、メソッド呼び出し元の値も変更されます。

メソッドの呼び出し

[編集]

メソッドは、その関連付けられた型の値(またはポインタ)に対して、ドット . を使って呼び出します。

// 値レシーバのメソッド呼び出し
value := MyStruct{Field: "value"}
value.MethodWithValue()

// ポインタレシーバのメソッド呼び出し
ptr := &MyStruct{Field: "value"}
ptr.MethodWithPointer()

Goは、値レシーバを持つメソッドをポインタ型の値に対しても自動的に呼び出すことができます(逆も同様)。これは、Goが暗黙的にアドレス演算子 & や間接参照演算子 * を行うためです。

メソッドの例

[編集]
package main

import "fmt"

// 構造体の定義
type Rectangle struct {
	Width  float64
	Height float64
}

// 値レシーバを持つメソッド (面積を計算)
func (r Rectangle) Area() float64 {
	return r.Width * r.Height
}

// ポインタレシーバを持つメソッド (幅をスケール)
func (r *Rectangle) ScaleWidth(factor float64) {
	r.Width *= factor
}

func main() {
	rect := Rectangle{Width: 10, Height: 5}

	// 値レシーバのメソッドを呼び出す
	area := rect.Area()
	fmt.Println("Area:", area) // Output: Area: 50

	// ポインタレシーバのメソッドを呼び出す
	rect.ScaleWidth(2)
	fmt.Println("Width after scaling:", rect.Width) // Output: Width after scaling: 20
	fmt.Println("Height:", rect.Height)             // Output: Height: 5
}

制限

[編集]

Goのメソッドは、メソッドが関連付けられる型が定義されているパッケージ内でのみ定義可能です。異なるパッケージで定義された型に対して、後からメソッドを追加することはできません。

例えば、標準パッケージ math で定義されている Float 型に対して、ユーザーが独自のパッケージ内で新しいメソッドを追加しようとしても、Goの言語仕様上、それは許可されません。

この制限は、以下の理由に基づいています。

  • 型の定義と振る舞いの局所性: 型の定義とその型に対する操作(メソッド)を同じパッケージ内にまとめることで、コードの可読性と保守性を高めます。型の振る舞いが分散することを防ぎ、コードの理解を容易にします。
  • 名前空間の管理: 異なるパッケージから同じ型のメソッドを自由に追加できると、名前の衝突が発生する可能性があり、プログラムの意図しない動作を引き起こす可能性があります。
  • コンパイルの効率性: 型の定義とメソッドの定義が同じパッケージ内にあることで、コンパイラは型の情報を一元的に管理し、効率的なコンパイル処理を行うことができます。

この制限のため、既存の型に新しい振る舞いを追加したい場合は、その型をラップした新しい型を定義し、その新しい型に対してメソッドを定義するなどの方法を取る必要があります。

メソッドの利点

[編集]
  • コードの組織化: 特定の型の振る舞いをその型の定義と近くに記述できるため、コードの可読性と保守性が向上します。
  • オブジェクト指向的な表現: 構造体とそれに関連するメソッドを組み合わせることで、オブジェクト指向プログラミングの概念に近い形でコードを記述できます。
  • インターフェースの実装: メソッドは、インターフェースを実装するために不可欠な要素です。ある型がインターフェースで定義されたすべてのメソッドを持つ場合、その型はそのインターフェースを実装しているとみなされます。

Goのメソッドは、型に固有の振る舞いを定義するための強力な機能であり、Goプログラミングにおいて重要な役割を果たします。値レシーバとポインタレシーバの使い分けを理解することが、効率的で意図した通りの動作をするコードを書く上で重要です。