[Golang] Method

2021-11-12 hit count image

GolangでMethod(メソッド)を定義して使う方法について説明します。

概要

今回のブログポストではGolangのMethod(メソッド)を定義して使う方法について詳しく説明します。このブログポストで紹介するコードは次のリンクで確認できます。

メソッド

Golangでメソッドは関数の種類で、タイプに所属されている関数をメソッドと言います。関数は一般的にタイプと独立されて存在しますが、メソッドはタイプに従属されます。Golangではどんなタイプもメソッドを持てることが出来ます。

Golangでメソッドは次のように定義します。

// func レシーバ メソッド名
func (r Person) greeting() string {
  return r.width * r.height
}

レシーバにはパッケージの全てのローカルタイプ(パッケージ中に宣言されたタイプ)が可能で、メソッドはレシーバに従属された関数になります。

これを確認するためmain.goファイルを生成して次のように修正します。

package main

import "fmt"

type Person struct {
  Name string
  Age  int
}

func (r Person) greeting() string {
  return "Hello, " + r.Name
}

func main() {
  p := Person{Name: "Rob", Age: 4}
  fmt.Println(p.greeting())
}

これを実行すると下記のような結果が表示されます。

# go run main.go
Hello, Rob

オブジェクト指向プログラミング

Golangはオブジェクト指向プログラミング言語(OOP, Object Oriented Programming language)です。ここでオブジェクト(Object)はデータ(State)と機能(Function)を持つことを意味します。

もし、Golangでメソッドが存在しない場合、データ(タイプ)と機能(関数)が別々存在することになります。そしたら、Golangではオブジェクトを生成することが出来ないし、オブジェクト指向プログラミングが出来ないことになります。

Golangではメソッドを使って、データに従属された機能を実装することができ、オブジェクト指向プログラミングができるようになります。また、メソッドは次のようにデータ間の関係も定義することが出来ます。

// Student <- Enroll -> Course
func (s * Student) Enroll(c *Course) {
  ...
}

// Student <- SendReport -> Professor, Report
func (s * Student) SendReport(p *Professor, r *Report) {
  ...
}

もちろん、関数を使ってもメソッドと同じ機能を実装することが出来ます。機能的には関数とメソッドは同じです。しかし、関数は渡されたデータを単純に演算だけすることで、メソッドはデータに従属されている概念を持てる違さがあります。

ポインタタイプメソッドと値タイプメソッド

Golangでプログラミングをすると、ポインタタイプメソッドと値タイプメソッドが存在することが分かります。Golangでは主にタイプの性格でポインタタイプメソッドと値タイプメソッドを区別して使ってます。

type Person struct {
  Name string
  Age  int
}

func (p *Person) AddAge() {
  p.Age += 1
}

例えば、Personと言うタイプがあります。Personタイプで作った人のデータの年齢が1歳上がっても、その人は同じ人なので、ポインタタイプメソッドを使います。

type Temperature float64

func (t Temperature) Up(temp float64) Temperature {
  return t + Temperature(temp)
}

逆にTemperatureと言うタイプは温度が上がると、以前の温度と上がった温度は概念的には違うので、値タイプメソッドを使うことが正しいです。

これを確認するためmain.goファイルを次のように修正します。

package main

import "fmt"

type Person struct {
  Name string
  Age  int
}

func (p *Person) AddAge() {
  p.Age += 1
}

type Temperature float64

func (t Temperature) Up(temp float64) Temperature {
  return t + Temperature(temp)
}

func main() {
  p1 := Person{Name: "Rob", Age: 4}

  fmt.Println("P1: ", p1)
  p1.AddAge()
  fmt.Println("P1: ", p1)

  t := Temperature(30.0)

  fmt.Println("T: ", t)
  t = t.Up(4.0)
  fmt.Println("T: ", t)
}

これを実行すると次のような結果が表示されます。

# go run main.go
P1:  {Rob 4}
P1:  {Rob 5}
T:  30
T:  34

コンストラクタとデストラクタ

Golangではコンストラクタとデストラクタがありません。したがって、コンストラクタが必要な場合、パッケージの中にコンストラクタように動作する関数を作って提供する必要があります。

これを確認するためgroup/main.goファイルを作って次のように修正します。

package group

type Member struct {
  Name string
  Age  int
}

func NewMember(name string, age int) *Member {
  return &Member{Name: name, Age: age}
}

ここでNewMember関数がコンストラクタの役割をする関数になります。

その後、次のコマンドを実行して、モジュールを生成します。

go mod init github.com/dev-yakuza/study-golang/method/group
go mod tidy

そして、これを実際使うmainパッケージを生成してみましょう。groupフォルダと同じ位置にmain/main.goファイルを生成して次のように修正します。

package main

import (
  "fmt"

  "github.com/dev-yakuza/study-golang/method/group"
)

func main() {
  member1 := group.Member{Name: "John", Age: 20}
  member2 := group.NewMember("Paul", 30)

  fmt.Println(member1)
  fmt.Println(member2)
}

そして次のコマンドを実行してモジュールを生成して、ローカルモジュールと連結します。

go mod init github.com/dev-yakuza/study-golang/method/main
go mod edit -replace github.com/dev-yakuza/study-golang/method/group=../group
go mod tidy

その後次のコマンドを実行して生成したプログラムを実行します。

go run main.go

そしたら、次のような結果が表示されます。

{John 20}
&{Paul 30}

Golangでモジュールを使う方法については下記のブログポストを参考してください。

Embedded field メソッド

Golangの構造体はEmbedded fieldを持つことが出来ます。Embedded fieldについて詳しい内容は下記のブログポストを参考してください。

次のように構造体がEmbedded fieldを持つことができて、Embedded fieldの構造体が次のようにメソッドを持ってる場合もあります。

type Member struct {
  Name string
  Age  int
}

func (p *Member) AddAge() {
  p.Age += 1
}

type Group struct {
  Member
  Grade int
}

この場合、次のようにEmbedded fieldのメソッドを使うことが出来ます。

g := Group{Member: Member{Name: "John", Age: 20}, Grade: 1}
g.AddAge()
fmt.Println(g)

これを確認するためmain.goファイルを次のように修正します。

package main

import "fmt"

type Member struct {
  Name string
  Age  int
}

func (p *Member) AddAge() {
  p.Age += 1
}

type Group struct {
  Member
  Grade int
}

func main() {
  g := Group{Member: Member{Name: "John", Age: 20}, Grade: 1}
  fmt.Println(g)
  g.AddAge()
  fmt.Println(g)
}

そして、これを実行すると次のような結果が表示されます。


# go run main.go
{{John 20} 1}
{{John 21} 1}

完了

これでGolangでメソッドを定義して使う方法について見て見ました。また、メソッドの特徴とコンストラクタを作る方法についても見て見ました。

私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!

Posts