Go言語のメソッドセット(Method sets)を見て行きます。
メソッドセットはtypeに結びつけられたメソッドを定めます。特定のtypeのメソッドがメソッドセットということですが、具体的なコードで動きをみていきましょう。
メソッドセット(Method sets)の値の処理
メソッドセットはその名の通りメソッドが関係するので、こちらのメソッドの内容も確認しておきましょう。
メソッドセットの動きには次のような特徴があります。
非ポインタのレシーバの場合、非ポインタ、ポインタ両方の値で機能します。
ポインタのレシーバの場合、ポインタの値でのみ機能します。
これらの動きの違いを具体的なコードで見て行きます。
レシーバと値にそれぞれポインタ、非ポインタをとって4つの組み合わせで動きを確認することができます。
非ポインタのレシーバの場合
順にパターンを分けて具体的なコードで見ていきましょう。コードにはインターフェイスも使っているので確認しておきましょう。
円の面積を求めるコードでやって行きましょう。
まずはレシーバに非ポインタをとる場合からです。
非ポインタのレシーバと非ポインタの値の処理
こちらは非ポインタの値の処理です。
package main
import (
"fmt"
"math"
)
type circle struct {
radius float64
}
type area interface {
calcArea() float64
}
func (c circle) calcArea() float64 {
return math.Pi * c.radius * c.radius
}
func calcResult(a area) {
fmt.Println("円の面積は", a.calcArea())
}
func main() {
c1 := circle{3}
calcResult(c1)
}
円周率を使うのMathパッケージをimportしています。
typeでcircle型のstructを作ります。フィールドに半径radiusをとりfloat64型としています。
インターフェイス型のareaを作って、メソッドに面積計算のcalcArea()を定義し、戻り値にfloat64を指定します。
このメソッドを、半径をレシーバ(ここでは非ポインタ)にとって定義しています。円の面積は円周率としてMath.Piを使って計算しています。
インターフェイスを引数にとってcalcResult()関数で、メソッドを実行して計算結果を出力します。
半径3を指定して、非ポインタとして値を計算しています。
計算結果はこうなります。
円の面積は 28.274333882308138
このコード自体は、インターフェイスで扱った処理と同じです。
非ポインタのレシーバとポインタの値の処理
今度はポインタの値の処理です。
上のコードを次のように変更します。
package main
import (
"fmt"
"math"
)
type circle struct {
radius float64
}
type area interface {
calcArea() float64
}
func (c circle) calcArea() float64 {
return math.Pi * c.radius * c.radius
}
func calcResult(a area) {
fmt.Println("円の面積は", a.calcArea())
}
func main() {
c2 := circle{3}
calcResult(&c2) // ポインタの値の処理
}
全体のコードは先ほどのコードと同じですが、計算時に渡す値が違います。
ここでは、ポインタの値を処理させるために&を付けています。
計算結果は同じです。
円の面積は 28.274333882308138
ポインタのレシーバの場合
今度は、レシーバにポインタをとる場合を見ていきましょう。
上のコードを修正して利用します。
ポインタのレシーバとポインタの値の処理
ポインタの値の処理を見て行きましょう。
次のコードになります。
package main
import (
"fmt"
"math"
)
type circle struct {
radius float64
}
type area interface {
calcArea() float64
}
func (c *circle) calcArea() float64 { // レシーバにポインタ
return math.Pi * c.radius * c.radius
}
func calcResult(a area) {
fmt.Println("円の面積は", a.calcArea())
}
func main() {
c3 := circle{3}
calcResult(&c3) // ポインタの値の処理
}
レシーバにポインタを渡しています。
処理にはポインタの値を渡しています。
計算結果は同じです。
円の面積は 28.274333882308138
ポインタのレシーバと非ポインタの値の処理
今度は非ポインタの値の処理を見て行きましょう。
次のコードになるのですが…
package main
import (
"fmt"
"math"
)
type circle struct {
radius float64
}
type area interface {
calcArea() float64
}
func (c *circle) calcArea() float64 {
return math.Pi * c.radius * c.radius
}
func calcResult(a area) {
fmt.Println("円の面積は", a.calcArea())
}
func main() {
c4 := circle{3}
calcResult(c4) // 非ポインタの値の処理
}
上のコードとの違いは、実行時の値の処理に、非ポインタの値を渡しているところです。
実行するとこうなります。
cannot use c4 (type circle) as type area in argument to calcResult:
circle does not implement area (calcArea method has pointer receiver)
このようにエラーになります。
最初に、「ポインタのレシーバの場合、ポインタの値でのみ機能します」とのべたことの確認になります。
これを処理するには、値の処理を次のように変更します。
func main() {
c4 := circle{3}
// calcResult(c4)
fmt.Println(c4.calcArea())
}
このように、インターフェイスの処理をすることで値を計算することができます。
28.274333882308138
最後に
ここでは、Go言語のメソッドセット(Method sets)を扱いました。
メソッドセットは特定のtypeに結びつけられたメソッドになります。
メソッドセットは、非ポインタのレシーバの場合は非ポインタ、ポインタ両方の値で機能し、ポインタのレシーバの場合はポインタの値でのみ機能します。