golang 方法
方法:
在函數聲明時,在其名字之前放上一個變量,即是一個方法。這個附加的參數會將該函數附 加到這種類型上,即相當於為這種類型定義了一個獨占的方法。
package main import "math" type Point struct { X,Y float64 } func Distance(p, q Point) float64 { return math.Hypot(q.X - p.X, q.Y - p.Y) } //Point 類型的方法 func(p Point) Distance(p, q Point) float64 { return math.Hypot(q.X - p.X, q.Y - p.Y) }
上面的代碼裏那個附加的參數p,叫做方法的接收器(receiver),早期的面向對象語言留下的遺 產將調用一個方法稱為“向一個對象發送消息”。
在能夠給任意類型定義方法這一點上,Go和很多其它的面向對象的語言不太一樣。因此 在Go語言裏,我們為一些簡單的數值、字符串、slice、map來定義一些附加行為很方便。方 法可以被聲明到任意類型,只要不是一個指針或者一個interface。
基於指針對象的方法
當調用一個函數時,會對其每一個參數值進行拷貝,如果一個函數需要更新一個變量,或者 函數的其中一個參數實在太大我們希望能夠避免進行這種默認的拷貝,這種情況下我們就需 要用到指針了。對應到我們這裏用來更新接收器的對象的方法,當這個接受者變量本身比較 大時,我們就可以用其指針而不是對象來聲明方法,如下:
func (p *Point) ScaleBy(factor float64){ p.X *= factor p.Y *= factor }
這個方法的名字是 (*Point).ScaleBy 。這裏的括號是必須的;沒有括號的話這個表達式可能 會被理解為 *(Point.ScaleBy) 。
在現實的程序裏,一般會約定如果Point這個類有一個指針作為接收器的方法,那麽所有Point 的方法都必須有一個指針接收器,即使是那些並不需要這個指針接收器的函數。
只有類型(Point)和指向他們的指針(*Point),才是可能會出現在接收器聲明裏的兩種接收器。 此外,為了避免歧義,在聲明方法時,如果一個類型名本身是一個指針的話,是不允許其出 現在接收器中的,比如下面這個例子:
type P *int func(P) f() { /* */ } //compile err
想要調用指針類型方法 (*Point).ScaleBy ,只要提供一個Point類型的指針即可,像下面這樣:
r := &Point{1, 2} r.ScaleBy(2) fmt.Println(*r) // "{2, 4}"
如果接收器p是 一個Point類型的變量,並且其方法需要一個Point指針作為接收器,我們可以用下面這種簡短 的寫法:
p.ScaleBy(2)
編譯器會隱式地幫我們用&p去調用ScaleBy這個方法。這種簡寫方法只適用於“變量”,包括 struct裏的字段比如p.X,以及array和slice內的元素比如perim[0]。我們不能通過一個無法取到 地址的接收器來調用指針方法,比如臨時變量的內存地址就無法獲取得到:
Point{1, 2}.ScaleBy(2) // compile error: can‘t take address of Point literal
總結一下:
1. 不管你的method的receiver是指針類型還是非指針類型,都是可以通過指針/非指針類型 進行調用的,編譯器會幫你做類型轉換。 2. 在聲明一個method的receiver該是指針還是非指針類型時,你需要考慮兩方面的內部,第 一方面是這個對象本身是不是特別大,如果聲明為非指針變量時,調用會產生一次拷 貝;第二方面是如果你用指針類型作為receiver,那麽你一定要註意,這種指針類型指向 的始終是一塊內存地址,就算你對其進行了拷貝。熟悉C或者C艹的人這裏應該很快能明 白。
golang 方法