type、メソッド、レシーバー【Go】
メソッド
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の説明をすれば解消されます。
参考