golang方法詳解

  Go 語言 類型方法是一種對類型行為的封裝 。Go 語言的方法非常純粹, 可以看作特殊類型的函數,其顯式地將對象實例或指針作為函數的第一個參數,並且參數可以自己指定,而不強制要求一定是 this或self。 這個對象實例或指針稱為方法的接收者 (reciever)。

 

方法聲明

為命名類型定義方法的語法格式如下:

// 類型方法接收者是值類型
func (t TypeName) MethodName (ParamList ) (Returnlist) { 
        // method body
}

// 類型方法接收者是指針
func (t *TypeName) MethodName (ParamList) (Returnlist) { 
        // method body
}

說明:

  • t 是接收者,可以自由指定名稱。
  • TypeName 為命名類型的類型名。
  • MethodName 為方法名,是 個自定義標識符。
  • ParamList 是形參列表。
  • ReturnList 是返回值列表。

  Go語言的類型型方法本質上就是一個函數,沒有使用隱式的指針,這是Go的優點,簡單明了。我們可以將類型的方法改寫成常規的函數。示例如下:

// 類型方法接收者是值類型
func TypName MethodName(t TypeName , otherParamList) (Returnlist) {
    //method body
}

// 類型方法接收者是指針
func TypName MethodName (t *TypeName , otherParamList) (Returnlist) {
    //method body 
}

// 示例
type SliceInt []int


// 定義一個 SliceInt的方法,實現累加切邊成員的功能
func (s SliceInt) Sum() int {
    sum := 0
    for _, i := range s{
        sum += i
    }
    return sum  
}

// 這個函數和上面的方法等價
func SliceInt_Sum(s SliceInt) int {
    sum := 0
    for _, i := range s {
        sum += i
    }  
    return sum
}


var s SliceInt = [] int {1, 2, ,3, 4}
// 使用方法訪問
s.Sum()
// 直接訪問
SliceInt_Sum(s)

類型方法有如下特點

  (1)可以為命名類型增加方法(除了接口),非命名類型不能自定義方法。 (非命令類型指型由預聲明類型、關鍵字和操作符組合而成的類型,例如數組、切邊、通道、指針、函數等) 

        比如不能為 [ ] int 類型增加方法,因為 [ ] int 是非命名類型。接口類型本身就是一個方法的簽名集合,所以不能為其增加具體的實現方法。

   (2)為類型增加方法有一個限制,就是方法的定義必須和類型的定義在同一個包中。

  不能再為 int、bool 等預聲明類型增加方法,因為它們是命名類型,但它們是 Go 語言內置的預聲明類型,作用域是全局的,為這些類型新增的方法是在某個包中,這與第(2)條規則衝突,所以Go編譯器拒絕為int增加方法。

   (3)方法的命名空間的可見性和變量一樣,大寫開頭的方法可以在包外被訪問,否則只能 在包內可見。

     (4)使用 type 定義的自定義類型是一個新類型,新類型不能調用原有類型的方法,但是底層類型支持的運算可以被新類型繼承。

type Map map[string)string

func (m Map) Print() { 
     // 類型支持的 range 運算 新類型可用
     for _, key := range m{
         fmt.Println(key)
     }
}        

type MyInt int

func main() {
    var  a  MyInt = 10
    var  b  MyInt = 10

   // int 類型支持的加減乘除運算,新類型同樣可用
   c  :=  a + b
   d  :=  a * b 

    fmt.Println("%d\n", c)
    fmt.Println("%d\n", d)
}   

 

方法調用  

總結一下方法調用,類型方法的一般調用方式:

TypeinstanceName.MethodName(ParamList) 
  • TypeinstanceName: 類型實例名或指向實例的指針變量名;
  • MethodName: 類型方法名;
  • ParamList: 方法實參。  

示例:

type T struct {
     a  int
}

func (t T) Get()  int {
    return t.a    
}

func (t *T) Set (i int) {
    t. a = i    
}

var t = &T{}

// 普通方法調用
t.Set(2)

// 普通方法調用
t.Get( )     

方法值( method value )

變量x的靜態類型是T, M是類型T的一個方法, x.T被稱為方法值(method value), x.T是一個函數類型變量,可以賦值給其他變量 。例如:

f : = x.M
f (args .. . ) 

等價於

x.M(args ... ) 

方法值(method value)其實是一個帶有閉包的函數變量,其底層實現原理和帶有閉包的匿名函數類似,接收值被隱式地綁定到方法值(method value)的閉包環境中。後續調用不需要再顯式地傳遞接收者。例如:

type T struct {
    a int
}

func (t  T) Get() int {
    return t.a
}

func (t  *T) Set (i int ) {
    t.a = i
}

func (t  *T) Print () { 
    fmt.Printf ("%p, %v, %d \n", t, t, t.a)    
}

var t =&T{} 

//method value
f := t.Set 

// 方法值調用
f(2)
t.Print()    //結果為 0xc4200140b8, &{2}, 2

//方法值調用, 接受值保存了方法值的環境
f(3)
t.Print()    //結果為 0xc4200140b8, &{3}, 3 

 

Tags: