Go言語マスター 〜メソッド・レシーバ〜
はじめに
Goのメソッドやレシーバについて解説します。
Methods
Goにはクラスの代わりに、型にメソッドを定義できます。
以下の例はX
, Y
の2つのメンバーを持ち、Abs
というメソッドが定義されています。メソッドは名前の前に(v Vertex)
というようにレシーバ引数を記述します。
初期化した後に、v.Abs
というようにしてメソッドを呼び出すことができます。
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())
}
Methods continued
上述のstruct
だけでなく、任意の型にメソッドを宣言できます。ただし、型が同じパッケージにある必要があります。
例えば、float64
にメソッドを定義したい場合、float64
は同パッケージ内で定義された型ではないのでtype MyFloat float64
というように定義しておく必要があります。
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
Pointer receivers
ポインタレシーバでメソッドを宣言できます。
ポインタレシーバーを用いることで、元の値に変更を加えるようなメソッドを作成できます。ポインタを使わない変数レシーバーの場合には、元の値のコピーがレシーバーとして渡されます。
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
Methods and pointer indirection
ポインタレシーバーScale
と、ポインタを引数に取るScaleFunc
関数を比べてみましょう。
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func ScaleFunc(v *Vertex, f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
この時、ポインタを引数に取るため、ポインタではない値を渡すとコンパイルエラーとなります。
var v Vertex
ScaleFunc(v, 5) // Compile error!
ScaleFunc(&v, 5) // OK
一方メソッドは、ポインタレシーバーである場合には自動的にポインタ渡しとして解釈してくれます。
var v Vertex
v.Scale(5) // OK
p := &v
p.Scale(10) // OK
この性質は逆も同じで、値を受け取る関数にポインタは渡せませんが、変数レシーバーにポインタを指定しても自動的に値渡しとして解釈されます。
Choosing a value or pointer receiver
値レシーバとポインタレシーバの使い分けですが、基本的にはポインタレシーバで統一すると良いとされます。
メソッドを呼び出す度にコピーが行われてメモリを消費することを避けたり、この後学習するインターフェースの利用のためにも便利です。
おわりに
今回は、Goでのメソッドやレシーバについて解説しました。やや高度な機能ですが、良い構成でコードを書くには欠かせない機能です。活用できるようになりましょう。