Go言語のメソッドとは?使い方まとめ

2025年4月17日木曜日

Go言語

Go言語のメソッドって、なんだか難しそう?大丈夫、基本からゆっくり見ていきましょう!

プログラミングには「関数」という命令をまとめたものがありますが、メソッドは特定のデータ(プログラミングの世界では「型」と呼びます)専用の特別な関数みたいなものです。

例えば、「犬」というデータがあったとします。「犬」専用の命令として「吠える()」や「尻尾を振る()」があったら便利ですよね。Go言語のメソッドは、まさにそんな感じで、特定の型に紐づいた処理を定義できる仕組みなんです。

オブジェクト指向という考え方の一部ですが、まずは「特定の型専用の関数なんだな」くらいの理解でOKです!

この記事を読むと、以下のことができるようになりますよ。

  • Go言語のメソッドの正体がわかる
  • メソッドの基本的な書き方が身につく
  • 値レシーバとポインタレシーバの違いと使い分けがわかる
  • 関数との違いがスッキリする
  • メソッドを使うときの注意点がわかる

Go言語メソッドの基本的な書き方を覚えよう

メソッドの正体がなんとなく掴めたら、次はどうやって書くのかを見ていきましょう。
Go言語のメソッドは、こんな形で書きます。

func (レシーバ変数 型) メソッド名(引数リスト) 戻り値リスト {
    // メソッドの処理内容
}

通常の関数定義 `func 関数名(引数リスト) 戻り値リスト { ... }` と似ていますが、`func` と `メソッド名` の間に `(レシーバ変数 型)` が入っているのが特徴です。
一つずつ見ていきましょう。

func
 関数やメソッドを定義するときの合言葉です。

(レシーバ変数 型)
 メソッドがどの型に属するかを指定します。「レシーバ」と呼ばれ、メソッドの中で自分自身のデータにアクセスするために使います。変数名は何でも良いですが、型の頭文字の小文字などがよく使われます (例 型 `User` なら `u`)。

メソッド名
 メソッドの名前です。どんな処理をするかわかりやすい名前を付けましょう。

(引数リスト)
 メソッドに追加で渡したい情報があれば書きます。なければ `()` だけ書きます。

戻り値リスト
 メソッドの処理結果を返す場合に書きます。なければ省略できます。

{ // メソッドの処理内容 }
 メソッドが実際に行う処理を波括弧 `{}` の中に書きます。

最初はちょっと複雑に見えるかもしれませんが、基本の形はいつも同じなので、何度か書いているうちに慣れてきますよ。

メソッドの要「レシーバ」を理解しよう

Go言語のメソッドを語る上で欠かせないのが「レシーバ」です。

さっきの書き方 `func (レシーバ変数 型) メソッド名(...) ...` の `(レシーバ変数 型)` の部分ですね。

レシーバは、メソッドと型を結びつける接着剤のような役割を持っています。
「このメソッドは、この型のためにあるんだよ!」と宣言している部分です。

そして、メソッドの中で、その型のデータ(例えば、犬の名前や年齢など)を使いたいときに、レシーバ変数(例 `u`)を通じてアクセスできます。

レシーバには大きく分けて2種類、「値レシーバ」と「ポインタレシーバ」があります。

この2つの違いを理解することが、Go言語のメソッドを使いこなす上でとてもポイントになります。

Go言語メソッドの使い方 - 値レシーバで基本を学ぶ

まずは「値レシーバ」から見ていきましょう。

値レシーバは、メソッドを呼び出すときに、元のデータのコピーがメソッドに渡される仕組みです。

例えるなら、元の書類をコピーして、そのコピーに対して作業するようなイメージです。コピーに変更を加えても、元の書類には影響がないですよね。

値レシーバのメソッドは、主に元のデータを変更する必要がない場合に使われます。
例えば、データの情報を表示するだけ、とか、計算結果を返すだけ、といった場合に向いています。

値レシーバのサンプルプログラム紹介

百聞は一見にしかず!実際のコードを見てみましょう。

ここでは、ユーザーの名前(Name)と年齢(Age)を持つ `User` という構造体を定義し、その情報を表示する `ShowInfo` というメソッドを作ってみます。

package main

import "fmt"

// ユーザー情報を表す構造体
type User struct {
    Name string
    Age  int
}

// 値レシーバを持つメソッド (ユーザー情報を表示)
func (u User) ShowInfo() {
    fmt.Printf("名前: %s, 年齢: %d歳\n", u.Name, u.Age)
}

func main() {
    // User型の変数を作成
    user1 := User{Name: "太郎", Age: 30}

    // ShowInfoメソッドを呼び出す
    user1.ShowInfo() // ドット(.)でメソッドを呼び出す

    // メソッド呼び出し後も、元のuser1の値は変わらない
    fmt.Printf("元のデータ - 名前: %s, 年齢: %d歳\n", user1.Name, user1.Age)
}

`func (u User) ShowInfo()` の `(u User)` が値レシーバです。`User` 型のデータ `u` を受け取っています。

上のプログラムを実行すると、こんな結果が表示されます。

名前: 太郎, 年齢: 30歳
元のデータ - 名前: 太郎, 年齢: 30歳

値レシーバのサンプルプログラム解説

コードと実行結果を見ていきましょう。
まず、`type User struct { ... }` で `User` というオリジナルのデータ型(構造体)を定義しました。

// 値レシーバを持つメソッド (ユーザー情報を表示)
func (u User) ShowInfo() {
    fmt.Printf("名前: %s, 年齢: %d歳\n", u.Name, u.Age)
}

ここで `ShowInfo` メソッドを定義しています。 `(u User)` が値レシーバで、`User` 型のデータを受け取ることを示しています。
メソッドの中では、レシーバ変数 `u` を使って、`User` の `Name` フィールドと `Age` フィールドにアクセスし、`fmt.Printf` で表示しています。

func main() {
    user1 := User{Name: "太郎", Age: 30}
    user1.ShowInfo() // メソッド呼び出し
    fmt.Printf("元のデータ - 名前: %s, 年齢: %d歳\n", user1.Name, user1.Age)
}

`main` 関数の中で、`user1` という `User` 型の変数を作り、「太郎」、30歳という情報を入れました。
そして `user1.ShowInfo()` のように、変数名の後にドット `.` を付けてメソッドを呼び出しています。
実行結果を見ると、`ShowInfo` メソッドはちゃんとユーザー情報を表示してくれていますね。
そして、メソッド呼び出しの後に `user1` の中身を表示しても、元のデータは全く変わっていないことが確認できます。値レシーバは元のデータのコピーを使うので、メソッド内で何か変更(今回はしていませんが)しても、呼び出し元の `user1` には影響しないのです。

Go言語メソッドの使い方- ポインタレシーバで値を変更する

次は「ポインタレシーバ」です。

値レシーバがコピーを扱ったのに対し、ポインタレシーバは元のデータの場所(アドレス)をメソッドに渡します。

例えるなら、書類そのものが置いてある場所を教えて、直接その書類に変更を加えてもらうイメージです。元の書類が直接書き換わります。

ポインタレシーバのメソッドは、元のデータを変更したい場合に使われます。
例えば、ユーザーの年齢を1つ増やしたり、設定を変更したりする場合に活躍します。

「ポインタ」と聞くと難しく感じるかもしれませんが、「元のデータを直接いじれるレシーバなんだな」と理解しておけば大丈夫です!

ポインタレシーバのサンプルプログラム紹介

では、ポインタレシーバのコードを見てみましょう。
先ほどの `User` 構造体に、年齢を1つ増やす `AddAge` というメソッドを追加します。

package main

import "fmt"

// ユーザー情報を表す構造体
type User struct {
    Name string
    Age  int
}

// 値レシーバを持つメソッド (ユーザー情報を表示)
func (u User) ShowInfo() {
    fmt.Printf("名前: %s, 年齢: %d歳\n", u.Name, u.Age)
}

// ポインタレシーバを持つメソッド (年齢を1増やす)
func (u *User) AddAge() {
    u.Age = u.Age + 1 // レシーバを通じて元のAgeを直接変更
}

func main() {
    // User型の変数を作成
    user1 := User{Name: "花子", Age: 25}

    fmt.Println("--- AddAgeメソッド呼び出し前 ---")
    user1.ShowInfo()

    // AddAgeメソッドを呼び出す
    user1.AddAge() // ドット(.)で呼び出すのは同じ

    fmt.Println("--- AddAgeメソッド呼び出し後 ---")
    user1.ShowInfo() // 再度表示して、年齢が変わったか確認
}

`func (u *User) AddAge()` の `(u *User)` がポインタレシーバです。型の前にアスタリスク `*` が付いていますね。これがポインタレシーバの目印です。

実行すると、今度はこうなります。

--- AddAgeメソッド呼び出し前 ---
名前: 花子, 年齢: 25歳
--- AddAgeメソッド呼び出し後 ---
名前: 花子, 年齢: 26歳

お、年齢がちゃんと増えていますね!

ポインタレシーバのサンプルプログラム解説

何が起こったのか、詳しく見てみましょう。

// ポインタレシーバを持つメソッド (年齢を1増やす)
func (u *User) AddAge() {
    u.Age = u.Age + 1 // レシーバを通じて元のAgeを直接変更
}

`AddAge` メソッドの定義です。レシーバが `(u *User)` と、型の前に `*` が付いています。これで「`User` 型のポインタを受け取りますよ」という意味になります。
メソッドの中では `u.Age = u.Age + 1` として、レシーバ `u` が指し示している元の `User` データの `Age` フィールドを直接変更しています。(Go言語ではポインタでも `.` でフィールドにアクセスできるのが便利なところです)

func main() {
    user1 := User{Name: "花子", Age: 25}

    fmt.Println("--- AddAgeメソッド呼び出し前 ---")
    user1.ShowInfo()

    user1.AddAge() // メソッド呼び出し

    fmt.Println("--- AddAgeメソッド呼び出し後 ---")
    user1.ShowInfo()
}

`main` 関数では、`user1` を作り、まず情報を表示します。

次に `user1.AddAge()` でメソッドを呼び出します。値レシーバのときと同じように、ドット `.` で呼び出せます。(Goがうまいことやってくれるんです)

そして、呼び出した後にもう一度 `user1.ShowInfo()` で表示すると… 実行結果の通り、年齢が25歳から26歳に増えています!

ポインタレシーバを使うと、このようにメソッドの中から呼び出し元のデータを変更できる、というわけです。これが値レシーバとの大きな違いですね。

Go言語メソッドと関数の違いを整理しよう

ここまでメソッドを見てきましたが、「あれ、普通の関数と何が違うの?」と思った方もいるかもしれません。

良い質問です!ここで違いをスッキリさせておきましょう。

大きな違いは、やはり「レシーバ」の有無です。

メソッド
 特定の型(レシーバの型)に紐づいている。
 その型のデータに対して何か操作をする、という目的で使われることが多い。
 呼び出すときは `変数名.メソッド名()` の形になる。

関数
 特定の型に紐づいていない。
 より汎用的な処理や、単独で意味を持つ処理に使われることが多い。
 呼び出すときは `関数名()` や `パッケージ名.関数名()` の形になる。

例えるなら、メソッドは「犬クラス」に属する「吠える()」機能、関数は誰でも使える「計算する()」機能、みたいなイメージでしょうか。
どちらが良い悪いではなく、目的に応じて使い分けるのがコツです。

Go言語メソッドを使う上での注意点

メソッド、便利そうですよね! でも、使う上でいくつか知っておくと良い点があります。

値レシーバ?ポインタレシーバ?どっちを使う?

迷ったら、まずはポインタレシーバを検討するのがGo言語では一般的です。

理由は、大きなデータをコピーするコストを避けられたり、メソッド内で一貫して値を変更できたりするためです。ただし、元の値を絶対に変更したくない、あるいは変更する必要がない場合は、値レシーバを選びましょう。

nilレシーバに注意

ポインタレシーバの場合、レシーバが `nil` (データが存在しない状態) でメソッドが呼ばれる可能性があります。メソッドの中で、レシーバが `nil` かどうかチェックする処理を入れると、予期せぬエラーを防げます。

func (u *User) SomeMethod() {
    if u == nil {
        fmt.Println("レシーバがnilです!")
        return // 何もせず終了
    }
    // uがnilでない場合の処理
    // ...
}

メソッド名はわかりやすく

どんな処理をするメソッドなのか、名前を見ただけでわかるようにしましょう。`ShowInfo` や `AddAge` のように、動詞を含めると分かりやすくなることが多いです。

同じ型のメソッドは一貫性を持たせる

ある型に対して、値レシーバのメソッドとポインタレシーバのメソッドが混在するのはOKですが、レシーバ変数の名前(例 `u`)は統一するとコードが読みやすくなります。

これらの点を頭の片隅に置いておくと、よりスムーズにメソッドを活用できるようになりますよ。

【まとめ】Go言語メソッドをマスターしてコードをレベルアップ!

今回はGo言語のメソッドについて、基本から使い方、注意点まで一通り見てきました。

・メソッドは特定の型に紐づいた特別な関数であること。
・`func (レシーバ) メソッド名() ...` という形で定義すること。
・レシーバには「値レシーバ」と「ポインタレシーバ」があり、元の値を変更するかどうかで使い分けること。
・関数とはレシーバの有無が大きな違いであること。

メソッドを理解し、使いこなせるようになると、Go言語のコードがより整理され、オブジェクト指向的な考え方を取り入れやすくなります。

最初は少し戸惑う部分もあるかもしれませんが、実際にコードを書いて動かしてみるのが一番の近道です!

【関連記事】 Go言語とは?特徴・できること・将来性

このブログを検索

  • ()

自己紹介

自分の写真
リモートワークでエンジニア兼Webディレクターとして活動しています。プログラミングやAIなど、日々の業務や学びの中で得た知識や気づきをわかりやすく発信し、これからITスキルを身につけたい人にも役立つ情報をお届けします。 note → https://note.com/yurufuri X → https://x.com/mnao111

QooQ