Caution
お使いのブラウザはJavaScriptが実行できない状態になっております。
当サイトはWebプログラミングの情報サイトの為、
JavaScriptが実行できない環境では正しいコンテンツが提供出来ません。
JavaScriptが実行可能な状態でご閲覧頂くようお願い申し上げます。
メソッド / レシーバ
Goでは関数を特定の型に紐づけることができ、これを「メソッド」と呼びます。メソッドの定義には「レシーバ」という特別な引数を使います。値レシーバとポインタレシーバの違いを理解することが重要です。
構文
// 値レシーバ(コピーに対して操作します。元の値は変更されません)
func (レシーバ変数 型名) メソッド名(引数) 戻り値型 {
// レシーバのフィールドを読み取れます。変更しても元の値は変わりません。
}
// ポインタレシーバ(元の値を変更できます)
func (レシーバ変数 *型名) メソッド名(引数) 戻り値型 {
// レシーバのフィールドを変更すると元の値に反映されます。
}
// メソッドの呼び出し
obj := 型名{}
obj.メソッド名() // 値・ポインタどちらでも自動変換されます。
ptr := &型名{}
ptr.メソッド名()
値レシーバ vs ポインタレシーバ
| 種類 | 概要 |
|---|---|
| 値レシーバ (T) | レシーバのコピーを受け取ります。元の値を変更しない読み取り専用のメソッドに向いています。 |
| ポインタレシーバ (*T) | レシーバのポインタを受け取ります。元の値を変更したい場合や、コピーコストを避けたい場合に使います。 |
| 自動変換 | Goは呼び出し時に必要に応じてアドレス取得・デリファレンスを自動で行います。 |
| インタフェース実装 | ポインタレシーバのメソッドは、ポインタ型のみがインタフェースを満たします。 |
サンプルコード
package main
import (
"fmt"
"math"
)
type Circle struct {
Radius float64
}
// 値レシーバ:cのコピーで動作します(元のCircleは変わりません)。
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
// ポインタレシーバ:元のCircleを変更できます。
func (c *Circle) Scale(factor float64) {
c.Radius *= factor
}
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r *Rectangle) Resize(w, h float64) {
r.Width = w
r.Height = h
}
// 文字列表現を返すメソッド(Stringer インタフェースの実装)
func (c Circle) String() string {
return fmt.Sprintf("Circle(radius=%.2f)", c.Radius)
}
func main() {
c := Circle{Radius: 5.0}
fmt.Println("円:", c)
fmt.Printf("面積: %.2f\n", c.Area())
fmt.Printf("周長: %.2f\n", c.Perimeter())
// ポインタレシーバのメソッドを値に対して呼ぶと自動的にアドレス取得します。
c.Scale(2.0) // (&c).Scale(2.0) と同じです。
fmt.Printf("2倍に拡大後の半径: %.2f\n", c.Radius)
fmt.Println()
rect := Rectangle{Width: 4, Height: 6}
fmt.Printf("長方形の面積: %.2f\n", rect.Area())
rect.Resize(10, 3)
fmt.Printf("リサイズ後: 幅=%.2f, 高さ=%.2f\n", rect.Width, rect.Height)
fmt.Printf("リサイズ後の面積: %.2f\n", rect.Area())
}
概要
Goのメソッドは構造体だけでなく、パッケージ内で定義した任意の型(組み込み型のエイリアスを含む)に追加できます。ポインタレシーバと値レシーバを同じ型で混在させることは可能ですが、インタフェースの実装においては注意が必要です。一般的にはポインタレシーバで統一することが推奨されます。
ポインタレシーバのメソッドを定義した場合、そのインタフェースを満たすのはポインタ型(*T)のみです。値型(T)はインタフェースを満たしません。インタフェースの変数に値型を代入するとコンパイルエラーになります。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。