メソッド
type定義した型に対する関数は、このように書くことができますが、
type Vertex struct { X, Y int } func Area(v Vertex) int { return v.X * v.Y }
次のように書くことで、オブジェクト指向ライクに表現できます。
type Vertex struct { X, Y int } func (v Vertex) Area() int { return v.X * v.Y }
これをメソッドと言います。
goにはclassがないため、あくまでもオブジェクト指向「ライク」ではありますが、
これによって、変数名を呼び出し元で入力すると、メソッド名が推測補完されるようになります。
定義したtypeとメソッドが紐づけられた状態です。
func main() { v := Vertex{5, 2} fmt.Println(v.Area()) // vとArea()が紐づいている }
レシーバー
メソッドと関連づけるstructについて、
値にする場合は、値レシーバー、
ポインタとする場合、ポインタレシーバーと呼ばれます。
func (v Vertex) Area() int { // vは値レシーバー return v.X * v.Y } func (v *Vertex) Scale(rate int) { // vはポインタレシーバー if v == nil { // ポインタレシーバのメソッドは、 nil ポインタ変数からでも呼び出しが可能なため、panicを引き起こさないよう回避する return } v.X *= rate v.Y *= rate }
typeの中身を変更する場合には、ポインタレシーバーである必要があります。
値レシーバーだと、コピーが作成され、コピーに対して処理されるからです。
(returnで値を返す(中身を変更しない)場合、値レシーバーの方が良いこともあります。参考資料1番目を参照。)
また、ポインタレシーバのメソッドは、 nil ポインタ変数からでも呼び出しが可能なため、panicを引き起こさないようif文で回避するべきです。
レシーバーに渡すことができるのは、パッケージ内でtype定義された型のみです。
typeとは、型や型リテラルに別名をつけることができる、予約語です。
typeで別名をつけられる型は次の通りです。
- 組み込み型(intやfloat64)
- パッケージ内の型(typeで定義されたもの)
- パッケージ外の型(typeで定義され、パッケージ名を指定して記述するもの)
- 型リテラル
- 関数
つまりは、次のようなこともできます。
type Height int // int型に別名Heightをつける func (h *Height) backHeight(x Height){ if h == nil { // ポインタレシーバのメソッドは、 nil ポインタ変数からでも呼び出しが可能なため、panicを引き起こさないよう回避する return } *h = *h * x } func main() { var h Height = 2 // Height型として宣言 fmt.Printf("%T %v\n", h, h) // main.Height 2 h.backHeight(h) fmt.Println(h) // 4 w := 4 fmt.Printf("%T %v\n", w, w) // int 4 Heightとして宣言しなければ、通常通りの型(この場合はint) }
関数を使うと、こんなこともできます。
type Func func() func (f Func) doubleF(){ f() f() } func main() { num := 1 var f Func = func(){ num++ } f.doubleF() // fが2度実行される fmt.Println(num) // 3 }
さらに次のように、引数をとることもできます。
type Func func(x int) //引数を持つ関数型を別名Funcと定義 func (f Func) doubleF(x int){ // このメソッド実行時に引数を指定する f(x) f(x) } func main() { num := 1 var f Func = func(x int){ // 引数の定義を、typeで定義したFuncと合わせる num += x } f.doubleF(3) // 引数に3を指定。fが2度実行される fmt.Println(num) // 7 }
type定義の補足
typeを定義してプログラムを実行すると、
comment on exported type ~ should be of the form "~ ..." (with optional leading article)
と注意が表示されているかもしれません。
その時は、このように、
// Func is ~ type Func func()
コメントアウトして、そのtypeの説明をすれば解消されます。
参考