Edited at

[Go]メソッド関連(レシーバー/起動/値レシーバー/ポインタレシーバー)についてのサンプル集


急ぎの方へ いきなり今回のサンプルコード全容


今回のサンプルコード全容

package main

import "fmt"

type user struct {
name string
height int
gender string
}

func (u user)show() {
fmt.Printf("名前:%s / 身長:%d / 性別:%s\n", u.name, u.height, u.gender)
}

func (u *user)update() {
u.height = 181
}

func main() {
u := user{name:"徳川", height:180, gender:"男"}
u.update() // 更新するメソッド(ポインタレシーバー)
u.show() // 出力するメソッド(値レシーバー)
}



Go言語でのメソッドとは


  • オブジェクト指向型に出てくるようなクラスや、その中で定義される「いわゆる一般的な」メソッドではない。

  • メソッドは、構造体などのデータに紐付いた関数。


レシーバーを書く

たとえば、下のuserという構造体に紐付けた、showメソッドを書きたい場合…


構造体「user」

type user struct {

name string
height int
gender string
}


書き方:構造体userに紐付いたメソッド

func (/*このメソッド内での変数名*/ /*紐付けたい構造体名*/) /*メソッド名*/() {

// ここに処理を書く
}

このように記述します。

少しややこしいので、具体例を出します。

今回は、ただ単にuserフィールドにアクセスして、出力するだけの処理を書きます。

もちろん、userという構造体に紐付けられています。


具体例:構造体「user」に紐付けた「show」メソッド

func (u user)show() {

fmt.Printf("名前:%s / 身長:%d / 性別:%s\n", u.name, u.height, u.gender)
}

このメソッド内では、uと書くことでuserフィールドへアクセスしています。


メソッドを起動させる


書き方:メソッドを起動させる

func /*何かのメソッドの中*/() {

// 何かの処理
/*フィールドをnewしたもの*/./*起動させたいメソッド名*/()

}


↑こんな感じです。

またしても分かりにくい感じになったので、具体例を出します。


具体例:メソッドを起動させる

func main() {

u := user{name:"徳川", height:180, gender:"男"}
u.show()
}

なお、user構造体は、一番上のものと同じです。

このように、u.show()とすることで、u(=user構造体)に紐付いた形で関数を使うことができる。


このままでは元データを更新できないので注意


元データを更新できない?

さきほど登場した、具体例↓


具体例:構造体「user」に紐付けた「show」メソッド

func (u user)show() {

fmt.Printf("名前:%s / 身長:%d / 性別:%s\n", u.name, u.height, u.gender)
}

↑では、user構造体をそのまま値渡ししている状態です。

この例のように、ただ出力するだけなら問題ないのですが、メソッドから元のデータを書き換えたい場合、これでは対応できません。

具体例です。

たとえば徳川くんの身長が1cm伸びてu.height = 181に変わったとしましょう。

ちょっと無茶な成長期になってしまいました。

そのためのメソッドを以下のように書きます。


具体例:身長が1cm伸びたので更新するための「update」メソッド

func (u user)update() {

u.height = 181 // u.heightを180から181に更新
}

これで以下のように実行しても…


具体例:「update」メソッドの起動

func main() {

u := user{name:"徳川", height:180, gender:"男"}
u.update()
fmt.Println(u) // => {徳川 180 男} ←身長が更新されていない…
}

このように、そのままuser型で引数を指定しても、本体のデータを更新することができず、身長は180cmのままで更新(変更)がされません。


元データを変更するには、引数をポインタにする

ポインタの指定の仕方は簡単です。

*を付け加えましょう。

具体例です。


具体例:「*」をつけてポインタレシーバーにしたメソッド

func (u *user)update() {

u.height = 181
}

↑こうすることで、データの本体が更新されます。


具体例:「*」をつけてポインタレシーバーにしたメソッド

func main() {

u := user{name:"徳川", height:180, gender:"男"}
u.update() // u.height = 181 に更新するメソッド
fmt.Println(u) // => {徳川 181 男} ←うまく更新された!
}

うまくいきました。

データそのものを変更させる場合には、ポインタレシーバーにしましょう。


注意! Go言語に参照渡しは「存在しない」

@c-yan さんに、何度かご指摘頂いている内容ですが、Go言語には参照渡しは存在しません。

まとめていただいている記事はこちらです。

僕の参考にしていた教材も、「…参照渡しにするには〜…」という表現を頻繁に使っており、みなさんが勘違いしやすい部分なのかもしれません。


まとめ

というワケで今回は長くなってしまいましたが、具体例として書いたコードをまとめると、以下の通りです。

(一番最初に出したコードと同じです。)


今回のサンプルコード全容

package main

import "fmt"

type user struct {
name string
height int
gender string
}

func (u user)show() {
fmt.Printf("名前:%s / 身長:%d / 性別:%s\n", u.name, u.height, u.gender)
}

func (u *user)update() {
u.height = 181
}

func main() {
u := user{name:"徳川", height:180, gender:"男"}
u.update() // 更新するメソッド(ポインタレシーバー)
u.show() // 出力するメソッド(値レシーバー)
}


最後までありがとうございました。