メソッドは、特別な_レシーバー_引数を持つ関数です。
レシーバーは、funcキーワードとメソッド名の間にある、専用の引数リストに現れます。
func (receiver type) MethodName(parameters) (returnTypes) {
}
メソッドを定義できるのは、そのメソッドと同じパッケージで定義された型をレシーバーに持つ場合だけです。
package person
type Person struct {
Name string
}
func (p Person) Greetings() string {
return fmt.Sprintf("Welcome %s!", p.Name)
}
構造体のメソッドは、ドット記法で呼び出すことができます。
p := Person{Name: "Bronson"}
fmt.Println(p.Greetings())
// Output: Welcome Bronson!
Personインスタンスpに対してメソッドGreetings()を呼び出した方法に注目してください。
これは、オブジェクト指向プログラミング言語でメソッドを呼び出す方法とまったく同じです。
覚えておきましょう。メソッドは、レシーバー引数を持つ関数にすぎません。 メソッドは名前の衝突を避けるのに役立ちます。メソッドは特定のレシーバー型に結び付いているので、異なる型に同じメソッド名を付けることができます。
import "math"
type rect struct {
width, height int
}
func (r rect) area() int {
return r.width * r.height
}
type circle struct {
radius int
}
func (c circle) area() float64 {
return math.Pow(float64(c.radius), 2) * math.Pi
}
レシーバーには、値レシーバーとポインターレシーバーの2種類があります。
これまでに見てきたメソッドはすべて値レシーバーを持っています。つまり、メソッドに渡された値のコピーを受け取るので、メソッド内でレシーバーに加えた変更は呼び出し元には見えません。
レシーバーが指す値を変更するには、ポインターレシーバーを持つメソッドを宣言できます。
これは型名の前に*を付けることで行います。
たとえばrect型の場合、ポインターレシーバーは*rectとして宣言します。
このような変更は、メソッドの呼び出し元からも見えます。
type rect struct {
width, height int
}
func (r *rect) squareIt() {
r.height = r.width
}
r := rect{width: 10, height: 20}
fmt.Printf("Width: %d, Height: %d\n", r.width, r.height)
// Output: Width: 10, Height: 20
r.squareIt()
fmt.Printf("Width: %d, Height: %d\n", r.width, r.height)
// Output: Width: 10, Height: 10
注:この演習はneed-for-speed演習の続きです。
この演習では、さまざまな種類のラジコンカーのレースを開催します。 それぞれの車には、独自の速度とバッテリー消費の特性があります。
車は満充電(100%)のバッテリーでスタートします。 リモコンで車を走らせるたびに、車は速度と同じメートル数だけ進み、バッテリーの残り割合が消費分だけ減ります。
車のバッテリーが消費量の割合を下回ると、その車はもう走らせられません。
ラジコンカーには、2つの情報を表示するおしゃれなLEDディスプレイが付いています:
"Driven <METERS> meters"。"Battery at <PERCENTAGE>%"。レースコースにはそれぞれ距離があります。 車は、バッテリー切れを起こさずにコースを完走できるかどうかで試されます。
CarにDriveメソッドを実装して、車の速度に応じて走行したメートル数を更新し、バッテリー消費に応じてバッテリーを減らします:
speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
car.Drive()
// car is now Car{speed: 5, batteryDrain: 2, battery: 98, distance: 5}
注:走らせた結果バッテリーが0を下回る場合は、その車を走らせないようにしてください。
CarにDisplayDistanceメソッドを実装して、LEDディスプレイに表示される距離をstringとして返します:
speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
fmt.Println(car.DisplayDistance())
// Output: "Driven 0 meters"
CarにDisplayBatteryメソッドを実装して、LEDディスプレイに表示されるバッテリー残量をstringとして返します:
speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
fmt.Println(car.DisplayBattery())
// Output: "Battery at 100%"
レースを完走するには、車がレースの距離を走りきれる必要があります。
つまり、ゴールラインを越える前にバッテリーを使い切らないということです。
CanFinishメソッドを実装してください。このメソッドはtrackDistance intを仮引数にとり、車がレースを完走できる場合はtrueを返し、そうでない場合はfalseを返します:
speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
trackDistance := 100
car.CanFinish(trackDistance)
// => true