###序章

関数を使用すると、ロジックを繰り返し可能なプロシージャに編成して、実行するたびに異なる引数を使用できます。 関数を定義する過程で、複数の関数が毎回同じデータに対して動作する可能性があることに気付くことがよくあります。 Goはこのパターンを認識し、メソッドと呼ばれる特別な関数を定義できます。この関数の目的は、レシーバーと呼ばれる特定のタイプのインスタンスを操作することです。 タイプにメソッドを追加すると、データが何であるかだけでなく、そのデータをどのように使用するかを伝えることができます。

メソッドの定義

メソッドを定義するための構文は、関数を定義するための構文に似ています。 唯一の違いは、メソッドのレシーバーを指定するために、funcキーワードの後にパラメーターを追加することです。 レシーバーは、メソッドを定義するタイプの宣言です。 次の例では、構造体タイプのメソッドを定義しています。

package main

import "fmt"

type Creature struct {
	Name     string
	Greeting string
}

func (c Creature) Greet() {
	fmt.Printf("%s says %s", c.Name, c.Greeting)
}

func main() {
	sammy := Creature{
		Name:     "Sammy",
		Greeting: "Hello!",
	}
	Creature.Greet(sammy)
}

このコードを実行すると、出力は次のようになります。

Output
Sammy says Hello!

NameおよびGreetingstringフィールドを持つCreatureという構造体を作成しました。 このCreatureには、Greetという単一のメソッドが定義されています。 受信者宣言内で、Creatureのインスタンスを変数cに割り当て、fmt.Printf

他の言語では、メソッド呼び出しの受信者は通常、キーワードによって参照されます(例: thisまたはself)。 Goは、レシーバーを他のレシーバーと同じように変数と見なすため、好きな名前を付けることができます。 コミュニティがこのパラメーターに推奨するスタイルは、レシーバータイプの最初の文字の小文字バージョンです。 この例では、レシーバータイプがCreatureであるため、cを使用しました。

mainの本体内に、Creatureのインスタンスを作成し、そのNameおよびGreetingフィールドに値を指定しました。 ここでは、型の名前とメソッドの名前を.と結合し、最初の引数としてCreatureのインスタンスを指定することにより、Greetメソッドを呼び出しました。

Goは、次の例に示すように、構造体のインスタンスでメソッドを呼び出す別のより便利な方法を提供します。

package main

import "fmt"

type Creature struct {
	Name     string
	Greeting string
}

func (c Creature) Greet() {
	fmt.Printf("%s says %s", c.Name, c.Greeting)
}

func main() {
	sammy := Creature{
		Name:     "Sammy",
		Greeting: "Hello!",
	}
	sammy.Greet()
}

これを実行すると、出力は前の例と同じになります。

Output
Sammy says Hello!

この例は前の例と同じですが、今回はドット表記を使用して、sammyに格納されているCreatureを使用してGreetメソッドを呼び出しました。受信者としての変数。 これは、最初の例の関数呼び出しの省略表記です。 標準ライブラリとGoコミュニティはこのスタイルを非常に好んでいるため、前に示した関数呼び出しスタイルはめったに見られません。

次の例は、ドット表記がより一般的である理由の1つを示しています。

package main

import "fmt"

type Creature struct {
	Name     string
	Greeting string
}

func (c Creature) Greet() Creature {
	fmt.Printf("%s says %s!\n", c.Name, c.Greeting)
	return c
}

func (c Creature) SayGoodbye(name string) {
	fmt.Println("Farewell", name, "!")
}

func main() {
	sammy := Creature{
		Name:     "Sammy",
		Greeting: "Hello!",
	}
	sammy.Greet().SayGoodbye("gophers")

	Creature.SayGoodbye(Creature.Greet(sammy), "gophers")
}

このコードを実行すると、出力は次のようになります。

Output
Sammy says Hello!! Farewell gophers ! Sammy says Hello!! Farewell gophers !

以前の例を変更してSayGoodbyeという別のメソッドを導入し、Greetを変更してCreatureを返すようにして、そのインスタンスでさらにメソッドを呼び出すことができるようにしました。 mainの本体では、最初にドット表記を使用し、次に機能呼び出しスタイルを使用して、sammy変数のメソッドGreetおよびSayGoodbyeを呼び出します。

どちらのスタイルも同じ結果を出力しますが、ドット表記を使用した例の方がはるかに読みやすくなっています。 ドットのチェーンは、メソッドが呼び出されるシーケンスも示します。ここで、機能スタイルはこのシーケンスを反転します。 SayGoodbye呼び出しにパラメーターを追加すると、メソッド呼び出しの順序がさらにわかりにくくなります。 ドット表記の明確さは、標準ライブラリとGoエコシステム全体にあるサードパーティパッケージの両方で、Goでメソッドを呼び出すための好ましいスタイルである理由です。

ある値で動作する関数を定義するのではなく、型でメソッドを定義することは、Goプログラミング言語にとって他の特別な意味を持っています。 メソッドは、インターフェースの背後にあるコアコンセプトです。

インターフェース

Goで任意の型にメソッドを定義すると、そのメソッドが型のメソッドセットに追加されます。 メソッドセットは、そのタイプにメソッドとして関連付けられ、Goコンパイラが使用して、あるタイプをインターフェイスタイプの変数に割り当てることができるかどうかを判断する関数のコレクションです。 インターフェースタイプは、タイプがそれらのメソッドの実装を提供することを保証するためにコンパイラーによって使用されるメソッドの仕様です。 インターフェイスの定義で見つかったものと同じ名前、同じパラメータ、同じ戻り値を持つメソッドを持つタイプは、そのインターフェイスを実装と呼ばれ、そのインターフェイスのタイプの変数に割り当てることができます。 以下は、標準ライブラリからのfmt.Stringerインターフェースの定義です。

type Stringer interface {
  String() string
}

fmt.Stringerインターフェースを実装する型の場合、stringを返すString()メソッドを提供する必要があります。 このインターフェイスを実装すると、fmtパッケージで定義された関数にタイプのインスタンスを渡すときに、タイプを希望どおりに正確に印刷できるようになります(「プリティプリント」と呼ばれることもあります)。 次の例では、このインターフェイスを実装するタイプを定義しています。

package main

import (
	"fmt"
	"strings"
)

type Ocean struct {
	Creatures []string
}

func (o Ocean) String() string {
	return strings.Join(o.Creatures, ", ")
}

func log(header string, s fmt.Stringer) {
	fmt.Println(header, ":", s)
}

func main() {
	o := Ocean{
		Creatures: []string{
			"sea urchin",
			"lobster",
			"shark",
		},
	}
	log("ocean contains", o)
}

コードを実行すると、次の出力が表示されます。

Output
ocean contains : sea urchin, lobster, shark

この例では、Oceanという新しい構造体タイプを定義しています。 Oceanimplementfmt.Stringerインターフェースと呼ばれます。これは、OceanStringというメソッドを定義しているためです。このメソッドはパラメーターを受け取らず、[ X157X]