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 

 

posted @ 2020-04-24 18:20  我的Blog要飞了  阅读(998)  评论(0编辑  收藏  举报