認真一點學 Go:13. 自定義型別和結構體 - 方法

瀟灑哥老苗發表於2021-10-12

收錄於 《Go 基礎系列》,作者:瀟灑哥老苗。

>>原文地址

學到什麼

  1. 什麼是方法?

  2. 如何呼叫方法?

  3. 什麼是值接收者和指標接收者?

  4. 如何使用 new 函式?

  5. 什麼是私有方法和公有方法?

概念

上篇學習了什麼是自定義型別,對於結構體也是自定義型別的一種,那方法是什麼?

如果一個函式屬於一個自定義型別時,那它被稱為方法,類似於物件導向中給類增加方法。

方法格式

在函式名前面寫上自己所屬的自定義型別後,這個函式就變為了該型別的方法。


type People ... 

func (p *People) SetName(name string) string {

     // ...

}
  • 第一行自定義了一個型別,名為 People

  • p *People 確定了 SetName 函式屬於*People型別,p 為型別的別名,也稱為接收者。p 類似面嚮物件語言中的 this , People 類似”class 類“。

  • 如果攜帶方法,自定義的型別不能為介面型別(interface{})和 指標。

注:*People 前面的“星號”確定了接收者為指標型別,稱為指標接收者,下面會講。

方法名稱

方法的名稱在型別的所有方法名稱和所有欄位名稱中必須是唯一的。就算相同的名稱一個是欄位一個是方法名也是不可以的。


type People struct {

    Name string

}

func (p People) Name() string {

    return p.Name

}

以上程式碼錯誤,名稱不唯一。

如果方法名稱和型別名稱相同是可以允許的。

方法呼叫

不管自定義的型別是基於內建型別還是結構體,都可以攜帶方法。


// 內建型別

type Num int

func (n Num) String() string {

    return fmt.Sprintf("%d", n)

}

// 結構體

type People struct {

    Name string

    Age  int

}

func (p People) GetName() string {

    return p.Name

}

以上程式碼中定義了兩個型別,每個型別分別攜帶了一個方法。

下來如何呼叫這兩個方法:


var n Num

n.String()

var p Peple

p.GetName()

先初始化好型別,然後再用“點”符號呼叫。

值接受者和指標接收者

1. 定義

在上面的程式碼中,是否注意到接收者型別有兩種,一種是帶星號(*People),一種是不帶的(People 和 Num)。

總結為:

  • 帶星號的稱為:指標接收者。

  • 不帶星號的稱為:值接收者。

還有一種特殊情況就是自定義的型別本身就是引用型別,就算接收者型別宣告中帶不帶”星號“它也屬於指標接收者。


type M map[string]string

func (m M) SetKey(key, val string) {

    (m)[key] = val

}

因為 M 型別依賴的是 map 型別,map 本身就是一個引用型別,因此 m 為指標接收者。

2. 區別

如果方法是值接收者,執行方法時接收者會被複製一份,即使方法修改了接收者的值也不是原來的一份。


func (p People) SetName(name string) string {

    p.Name = name

    return name

}

初始化 People 修改 Name 欄位。


p1 := People{Name: "苗"}

p1.SetName("瀟灑哥")

fmt.Println(p1.Name)

// 輸出

發現了沒,雖然呼叫了方法進行了修改,但還是不生效。因為 p1 和接收者 p 已經不是一個值了。如果想修改生效,只需把值接收者改為指標接收者。


func (p *People) SetName(name string) string {

    p.Name = name

    return name

}

3. 呼叫時型別轉化

在呼叫方法時,不管是值接收者還是指標接收者,呼叫時的變數型別是否是指標是不影響的。

例如:People 結構體的方法 SetName 不管是指標還是值接收者,以下程式碼都可以呼叫。


// 值

p1 := People{}

p1.SetName()

// 指標

p2 := &People{}

p2.SetName()

至於為什麼?當編譯器發現你呼叫的變數( p1 和 p2 )型別和接收者的型別不相同時,也就是一個是指標一個不是,這個時候就會自動轉化。

至於接收者的值被方法修改時結果會不會改變,和呼叫變數的型別沒關係。

New 函式使用

當初始化一個指標變數時,可以使用 “&” 符號,也可以使用 new 函式。


new(T) 

例如,將 p2 := &People{} 修改。


p2 := new(People)

p2.Name = "老苗"

new(People)&People{} 等價。

私有和公有

方法名大寫字母開頭公有,小寫字母開頭私有。如果方法所在的包和呼叫者不是同一個,那私有方法是不能被呼叫的,只能呼叫公有方法。

私有方法只能在同一個包內被呼叫。

總結

本篇文章完了之後,自定義型別和結構體的知識點就講完了,如果看到了這就給自己點個贊,堅持住!!!

本作品採用《CC 協議》,轉載必須註明作者和本文連結
瀟灑哥老苗

相關文章