Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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

今回のサンプルコード全容
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() // 出力するメソッド(値レシーバー)
}

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

riotam
僕が学んだことや困ったことを共有して、誰かの役に立てれば嬉しいです。 わずかでも、世の中を便利にしたいと思ってます。 ペーペーですが、よろしくお願いします。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした