A Tour of Go vol.6 (Methods/Receiver)
Methods
Goはクラスの仕組みを持たないが型にメソッドを持つことができる。
メソッドは引数を
type Vertex struct { X, Y float64 } func (v Vertex) Abs() float64 { return math.Sqrt(v.X+v.X + v.Y*v.Y) } func main() { v := Vertex{3, 4} fmt.Println(v.Abs()) }
Vertex型はレシーバ引数によってメソッドAbs()を持つ。
レシーバー: メソッドを呼び出される対象
クラスでメソッドを実装してきた身としてはメソッドありきでどの型に属すかを引数で決めるという順番はすごく慣れない。
レシーバーは値型もポインタ型も取ることができる。
ポインタ型
type Vertex struct { X, Y float64 } func (v Vertex) Abs() float64 { fmt.Println(v) //{30 40} return math.Sqrt(v.X*v.X + v.Y*v.Y) } func (v *Vertex) Scale(f float64) { v.X = v.X * f //3*10=30 v.Y = v.Y * f //4*10=40 } func main() { v := Vertex{3, 4} v.Scale(10) fmt.Println(v.Abs()) }
ポインタ型ではmain関数で宣言したVertext変数を操作する
値型
type Vertex struct { X, Y float64 } func (v Vertex) Abs() float64 { fmt.Println(v) //{3 4} return math.Sqrt(v.X*v.X + v.Y*v.Y) } func (v Vertex) Scale(f float64) { v.X = v.X * f v.Y = v.Y * f } func main() { v := Vertex{3, 4} v.Scale(10) fmt.Println(v.Abs()) }
値型ではVertext変数のコピーを操作する
type Vertex struct { X, Y float64 } // メソッドがポインタレシーバ func (v *Vertex) Scale(f float64) { fmt.Println(v) v.X = v.X * f v.Y = v.Y * f fmt.Println(v) } func ScaleFunc(v *Vertex, f float64) { v.X = v.X * f v.Y = v.Y * f } func main() { v := Vertex{3, 4} // vは値型 v.Scale(2) // ScaleFuncの第一引数はポインタ型なので&v ScaleFunc(&v, 10) p := &Vertex{4, 3} // pはポインタ型 p.Scale(3) // ScaleFuncの第一引数はポインタ型でpはもともとポインタ型 ScaleFunc(p, 8) fmt.Println(v, p) }
type Vertex struct { X, Y float64 } // 値型のレシーバー func (v Vertex) Abs() float64 { fmt.Println(v) return math.Sqrt(v.X*v.X + v.Y*v.Y) } func AbsFunc(v Vertex) float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } func main() { v := Vertex{3, 4} fmt.Println(v.Abs()) fmt.Println(AbsFunc(v)) // pはポインタ型 p := &Vertex{4, 3} // Absは値型のレシーバだけどポインタ型も受け取る fmt.Println(p.Abs()) fmt.Println(AbsFunc(*p)) }
主なポインタレシーバを使う2つの理由
- メソッドでレシーバが指す先の変数を変更させたい
- メソッドの呼び出し毎に変数のコピーを避けたい
レシーバーを値型/ポインタ型使い分ける考え
qiita.com