09.02-Method

Methods

雖然現在的程式碼比初版好了許多,不過我們還可以用一個所謂的 method 特殊型別來大幅改善程式碼:

func (c *Circle) area() float64 {
    return math.Pi * c.r*c.r
}

在 func 關鍵字與函式名稱之間,我們已經新增了一個 “receiver”,receiver 就像是一個參數,具有名稱與型別,但透過此方式建立函式可讓我們使用 . 運算符來呼叫函式:

fmt.Println(c.area())

這樣很易於閱讀,我們不再需要 & 運算符(Go 語言能自動知道要傳遞一個指標給此 method 的 circle),而且因為此函式只能用在 Circle,所以我們可以重新將函式只命名為 area

現在替 rectangle 做同樣的設計:

type Rectangle struct {
    x1, y1, x2, y2 float64
}
func (r *Rectangle) area() float64 {
    l := distance(r.x1, r.y1, r.x1, r.y2)
    w := distance(r.x1, r.y1, r.x2, r.y1)
    return l * w
}

main 具有:

r := Rectangle{0, 0, 10, 10}
fmt.Println(r.area())

嵌入式型別

一個 struct 的欄位通常代表有一個關係,例如:一個 Circle 有一個 radius,假設我們有一個 person 這樣的 struct:

type Person struct {
    Name string
}
func (p *Person) Talk() {
    fmt.Println("Hi, my name is", p.Name)
}

而且我們想要建立一個新的 Android struct,我們可以這樣寫:

type Android struct {
    Person Person
    Model string
}

這樣可以運作,不過我們不會說一部 Android 是一個人,也不會說一部 Android 有一個人。Go 語言透過嵌入式型別(embedded type)提供這類關係,也就是所謂的匿名欄位(anonymous field),嵌入式型別看起來類似這樣:

type Android struct {
    Person
    Model string
}

我們使用(Person)型別,而且不幫它取名字。當以這個方式定義時,就可以使用這個型別名稱來存取 Person struct:

a := new(Android)
a.Person.Talk()

不過我們也可以直接在 Android 上呼叫任何的 Person method:

a := new(Android)
a.Talk()

這是一個直觀的關係:人可以說話,而一個 android 是一個人,因此 android 可以說話。

Comments