🙆

Golang メソッド

2021/08/03に公開

Golang メソッド

基本

  • Go のメソッドは特殊な関数
  • 関数名の前にレシーバーと呼ばれるパラメーターを含めたもの
  • メソッドはカスタム型に関連づける事ができる

メソッドの宣言

構文

func (variable type) MethodName(parameters ...) {
    // メソッドの処理を記述
}

具体的な例

type person struct {
	firstName string
	lastName  string
}

func (p person) fullName() string {
	return p.firstName + " " + p.lastName
}

使用例

func main() {
	p := person{"Ryo", "Sato"}
	fmt.Println("FullName:", p.fullName())
}

インスタンスの変数を書き換える

次のようにレシーバーの変数をポインターにする

func (p *person) renameFirstName(newFirstName string) {
	p.firstName = newFirstName
}
func main() {
	p := person{"Ryo", "Sato"}
	p.renameFirstName("Taro")
	fmt.Println("FullName:", p.fullName())
}
  • Goの規則では、構造体のいずれかのメソッドにポインターレシーバーがある場合、その構造体のすべてのメソッドにポインターレシーバーが必要と定められている。

型を派生させ、メソッドを宣言する

  • 別のパッケージに属する型から構造体を定義する事はできない
  • 基本型からカスタム型を作成し、基本型であるかのように使用することはできる
type upperstring string

func (s upperstring) Upper() string {
    return strings.ToUpper(string(s))
}

メソッドの再利用

メソッドをもつ構造体を別の構造体に埋め込む事で、埋め込み先でも同じメソッドを再利用できる

type japanesePerson struct {
    person
}
func main() {
	p := japanesePerson{person{"Ryo", "Sato"}}
	fmt.Println("FullName:", p.fullName())
}

一見してOOPで言うところの継承の動作に見えるが、継承ではない。
次のようにラッパーメソッドを作る事で初めて埋め込み先のメソッドとして機能し、それまではあくまで埋め込み元のメソッドである

func (p japanesePerson) fullName() string {
	return p.fullName()
}

メソッドのオーバーロード

メソッドはレシーバーに固有の為、メソッドのレシーバーを埋め込み先の構造体に変更する事で実現できる

func (p japanesePerson) fullName() string {
	return p.lastName + " " + p.firstName
}

また、埋め込み元の構造体のメソッドを明示的にして呼び出す事もできる

func main() {
	p := japanesePerson{person{"Ryo", "Sato"}}
	fmt.Println("FullName:", p.fullName())
	fmt.Println("FullName:", p.person.fullName())
}

実行結果

FullName: Sato Ryo
FullName: Ryo Sato

カプセル化

  • メソッドを公開にするには大文字の識別子だけを、メソッドを非公開にするには子文字の識別子を使用する
  • 別のパッケージから実装の詳細のみを非公開にできる
package human

type Person struct {
	firstName string
	lastName  string
}

func (p Person) FullName() string {
	return p.firstName + " " + p.lastName
}

func (p *person) renameFirstName(newFirstName string) {
	p.firstName = newFirstName
}
func main() {
	p := human.Person{"Ryo", "Sato"}
	fmt.Println("FullName:", p.FullName())
	p.renameFirstName("Taro")// エラーになる
	fmt.Println("FullName:", p.FullName())
}
GitHubで編集を提案

Discussion