急ぎの方へ いきなり今回のサンプルコード全容
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
メソッドを書きたい場合…
type user struct {
name string
height int
gender string
}
func (/*このメソッド内での変数名*/ /*紐付けたい構造体名*/) /*メソッド名*/() {
// ここに処理を書く
}
このように記述します。
少しややこしいので、具体例を出します。
今回は、ただ単にuserフィールドにアクセスして、出力するだけの処理を書きます。
もちろん、userという構造体に紐付けられています。
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構造体)
に紐付いた形で関数を使うことができる。
このままでは元データを更新できないので注意
元データを更新できない?
さきほど登場した、具体例↓
func (u user)show() {
fmt.Printf("名前:%s / 身長:%d / 性別:%s\n", u.name, u.height, u.gender)
}
↑では、user構造体をそのまま値渡ししている状態です。
この例のように、ただ出力するだけなら問題ないのですが、メソッドから元のデータを書き換えたい場合、これでは対応できません。
具体例です。
たとえば徳川くんの身長が1cm伸びてu.height = 181
に変わったとしましょう。
ちょっと無茶な成長期になってしまいました。
そのためのメソッドを以下のように書きます。
func (u user)update() {
u.height = 181 // u.heightを180から181に更新
}
これで以下のように実行しても…
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() // 出力するメソッド(値レシーバー)
}
最後までありがとうございました。