在Go語言中,結構體就像是類的一種簡化形式,那麼類的方法在哪裡呢?在Go語言中有一個概念,它和方法有著同樣的名字,並且大體上意思相同,Go 方法是作用在接收器(receiver)上的一個函數,接收器是某種型別的變數,因此方法是一種特殊型別的函數。
接收器型別可以是(幾乎)任何型別,不僅僅是結構體型別,任何型別都可以有方法,甚至可以是函數型別,可以是 int、bool、string 或陣列的別名型別,但是接收器不能是一個介面型別,因為介面是一個抽象定義,而方法卻是具體實現,如果這樣做了就會引發一個編譯錯誤
invalid receiver type…
。
接收器也不能是一個指標型別,但是它可以是任何其他允許型別的指標,一個型別加上它的方法等價於物件導向中的一個類,一個重要的區別是,在Go語言中,型別的程式碼和系結在它上面的方法的程式碼可以不放置在一起,它們可以存在不同的原始檔中,唯一的要求是它們必須是同一個包的。
型別 T(或 T)上的所有方法的集合叫做型別 T(或 T)的方法集。
因為方法是函數,所以同樣的,不允許方法過載,即對於一個型別只能有一個給定名稱的方法,但是如果基於接收器型別,是有過載的:具有同樣名字的方法可以在 2 個或多個不同的接收器型別上存在,比如在同一個包裡這麼做是允許的。
提示
在物件導向的語言中,類擁有的方法一般被理解為類可以做的事情。在Go語言中“方法”的概念與其他語言一致,只是Go語言建立的“接收器”強調方法的作用物件是接收器,也就是類範例,而函數沒有作用物件。
為結構體新增方法
本節中,將會使用背包作為“物件”,將物品放入背包的過程作為“方法”,通過程序導向的方式和Go語言中結構體的方式來理解“方法”的概念。
1) 程序導向實現方法
程序導向中沒有“方法”概念,只能通過結構體和函數,由使用者使用函數引數和呼叫關係來形成接近“方法”的概念,程式碼如下:
type Bag struct {
items []int
}
// 將一個物品放入背包的過程
func Insert(b *Bag, itemid int) {
b.items = append(b.items, itemid)
}
func main() {
bag := new(Bag)
Insert(bag, 1001)
}
程式碼說明如下:
-
第 1 行,宣告 Bag 結構,這個結構體包含一個整型切片型別的 items 的成員。
-
第 6 行,定義了 Insert() 函數,這個函數擁有兩個引數,第一個是背包指標(*Bag),第二個是物品 ID(itemid)。
-
第 7 行,用 append() 將 itemid 新增到 Bag 的 items 成員中,模擬往背包新增物品的過程。
-
第 12 行,建立背包範例 bag。
-
第 14 行,呼叫 Insert() 函數,第一個引數放入背包,第二個引數放入物品 ID。
Insert() 函數將 *Bag 引數放在第一位,強調 Insert 會操作 *Bag 結構體,但實際使用中,並不是每個人都會習慣將操作物件放在首位,一定程度上讓程式碼失去一些正規化和描述性。同時,Insert() 函數也與 Bag 沒有任何歸屬概念,隨著類似 Insert() 的函數越來越多,程序導向的程式碼描述物件方法概念會越來越麻煩和難以理解。
2) Go語言的結構體方法
將背包及放入背包的物品中使用Go語言的結構體和方法方式編寫,為 *Bag 建立一個方法,程式碼如下:
type Bag struct {
items []int
}
func (b *Bag) Insert(itemid int) {
b.items = append(b.items, itemid)
}
func main() {
b := new(Bag)
b.Insert(1001)
}
第 5 行中,Insert(itemid int) 的寫法與函數一致,(b*Bag) 表示接收器,即 Insert 作用的物件範例。
每個方法只能有一個接收器,如下圖所示。
圖:接收器