Go語言方法和接收器

2020-07-16 10:05:12
在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 作用的物件範例。

每個方法只能有一個接收器,如下圖所示。


圖:接收器