【第十回Go言語学習備忘録】Structを使用してメソッドを作成する!

2月 12, 2022

どもです。awsからre:event記念パーカーが届きました。完全に忘れていた頃にきた国際便にビビるsaisaiです。


今回はStructを使用してメソッドを作成してみます。Structに関する記事はこちらからお読みいただけます!


関数とは似て非なるメソッドについて今回の記事の執筆を通してしっかりと理解を深めていきたいと思います。

メソッドとはなんぞや

メソッドとは"特定の構造体などを関連づけて定義する関数"のことです。といってもこれだけの説明では、普通の関数とメソッドで何が違うのかわかりにくいので、実際にコードに落とし込んで理解していきたいと思います。

package main

import "fmt"

type Define struct {
	X int
	Y int
	// X,Y int でも可
}

func Add(v Define) int {
	return v.X + v.Y
}
//普通の関数

func (v Define) Add() int {
	return v.X + v.Y
}
//メソッド1

func (v Define) Multi() int {
	return v.X * v.Y
}
//メソッド2

func main() {
	num := Define{5, 10}
	fmt.Println(Add(num)) //15
	fmt.Println(num.Add()) //15
	fmt.Println(num.Multi()) //50
}

それぞれの違いを見ていきます。まずは定義方法から、関数の場合は

func Add(v Define) int { //func 関数名(引数) 型
	return v.X + v.Y
}

と定義するのに対し、メソッドは

func (v Define) Add() int { //func (値レシーバ) メソッド名() 型
	return v.X + v.Y
}

といった感じで定義するようです。値レシーバには構造体やインターフェースの値が入ります。


では、どうして関数とメソッドの二つが存在するのでしょうか。例えばmain関数をご覧ください。

func main() {
	num := Define{5, 10}
	fmt.Println(Add(num))
	fmt.Println(num.Add())
}

一見通常の関数の方が呼び出し方は簡単に思えるかもしれません。しかし、それは"Add関数がどこかに定義されている"ということがわかっている前提のことです。コードが多岐に渡った場合、そう簡単に呼び出す関数を特定することはできません。

しかし、メソッドの場合は

レシーバ.変数名

で呼び出すことができます。上記コードの場合は"num."と入力した段階で定義されたメソッドが予測変換で表示されるので、メソッドをどこで定義したかはわざわざ意識することなく呼び出せます。


一度Structを定義してしまえば、Structの値レシーバを利用して複数のメソッドをを用意し、メソッド名を変更するだけで臨機応変に呼び出すことができます。


今回のコードの場合ですと、"num."と入力するだけでメソッド1とメソッド2が予測変換に現れます。

ポインタレシーバを利用してメソッドの値を変更する。

ポインタレシーバを使用することでメソッドの値を変更することができます。

package main

import "fmt"

type Define struct {
	depth int
	width int
	// Depth, Width int でも可
}

func (v Define) Area() int {
	return v.depth * v.width
}

func (v *Define) Scale(i int) {
	v.depth = v.depth * i
	v.width = v.width * i
}

func main() {
	num := Define{3, 5}
	fmt.Println(num.Area()) //15
	num.Scale(10)
	fmt.Println(num.Area()) //1500
}

これまで同様以下のような構造体を定義し、

type Define struct {
	depth int
	width int
	// Depth, Width int でも可
}

まずは値レシーバを利用して以下のようなメソッドを定義しました。

func (v Define) Area() int {
	return v.depth * v.width
}

そしてStructの値を定義し、これまで通り出力していきます。

num := Define{3, 5}
fmt.Println(num.Area()) //15

では、Structの値を変更してもう一度メソッドを呼び出してみましょう。今回は、既存のStructの値を10倍するように定義してみたいと思います。

func (v *Define) Scale(i int) {
	v.depth = v.depth * i
	v.width = v.width * i
} 
//構造体の値(depth,width)がそれぞれ変更される。

メソッド名は"Scale"としました。構造体の値を変更する場合はポインタでレシーバを定義する必要があります。これをポインタレシーバと言います。

Scaleメソッドの引数でStructの値を変更しましょう。例えば今回の場合は、

num.Scale(10)

このようにメソッドを実行します。"i=10″とする引数をとったScaleメソッドが実行され、"v.depth"および"v.width"の値がそれぞれ10倍されます。では、あらためてAreaメソッドを呼び出してみましょう。

fmt.Println(num.Area()) //1500

すると、もともとのメソッドで定義した値の10倍の値を出力するようになりました!

ひとこと

今回はメソッドの定義方法について学びました。Structの知識も合わせて1upしましたね!


これで使用する関数やメソッドの中身についても追求できるようになってきたとおもいますので、関数やメソッドを無意識に使って終わりではなく、できる限りその中身についても調べていきたいと思います。


ここまで読んでいただきありがとうございました!


-saisai-


↓本日のオススメ

Golang

Posted by CY