公式の A Tour of Go ではじめて Go 言語を触ってみました。
前回の記事(基本編)はこちら
以下、メソッドについて学んだことのメモ。
メソッド
基本
- Go にはクラスの仕組みはないが、型にメソッドを定義できる
- メソッドは、レシーバを引数にとる
- レシーバはメソッドを定義する対象となる型
- 構造体だけでなく任意の型にメソッドを定義できる
type MyFloat float64
- 構造体だけでなく任意の型にメソッドを定義できる
- レシーバを伴うメソッドを宣言するには、レシーバの型が同じパッケージ内に存在しなければならない
- レシーバはメソッドを定義する対象となる型
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
// Vertex 型のメソッドを定義
// レシーバ: v Vertex
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()) // メソッドを使用
}
- 同様の処理を、通常の関数として記述することも可能
func Abs(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(Abs(v))
}
ポインタレシーバ
- ポインタレシーバを持つメソッドは、レシーバが指す変数を更新できる
- レシーバ自身を更新する機会が多いため、変数レシーバよりもポインタレシーバのほうが一般的
- 変数レシーバを持つメソッド: 変数のコピーを操作(関数の引数と同じ)
- ポインタレシーバを持つメソッド: 変数そのものを操作
- ポインタレシーバを用いることにより、メソッドの呼び出し毎に発生してしまう変数のコピーを避けることができる
- レシーバが大きな構造体である場合に有効
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
// 変数レシーバを持つメソッド
func (v Vertex) Abs() float64 {
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) // ポインタレシーバを持つメソッドの実行 => 変数 v 自身に対する操作
// Go では利便性のために v.Scale(10) を (&v).Scale(10) として解釈する
fmt.Println(v.Abs()) // 変数レシーバを持つメソッドの実行 => 変数 v のコピーに対する操作
}
- ポインタレシーバを持つメソッドも、関数として書き換えられる
func Abs(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func Scale(v *Vertex, f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4}
Scale(&v, 10) // &v: 変数 v へのポインタ
fmt.Println(Abs(v)) // 50
}
次回