方法声明 1 2 3 4 func  (receiver *T或T)   MethodName(参数列表) (返回值列表) {      }
 
receiver 是方法与类型之间的纽带,也是方法与函数的最大不同。无论 receiver 参数的类型为 *T 还是 T,都把一般声明形式中的 T 叫做 receiver 参数 。如果 t 的类型为 T,那么说这个方法是类型 T 的一个方法,如果 t 的类型为 *T,那么就说这个方法是类型 *T 的一个方法。每个方法只能有一个 receiver 参数。
receiver 部分的参数名不能与方法参数列表中的形参名,以及具名返回值中的变量名存在冲突,必须在这个方法的作用域中具有唯一性。
1 2 3 4 type  T struct {}func  (t T)   M(t string ) {  }
 
receiver 参数的基类型不能是指针类型或接口类型。
1 2 3 4 5 6 7 8 9 type  MyInt *int func  (r MyInt)   String() string  {      return  fmt.Sprintf("%d" , *(*int )(r)) }type  MyReader io.Readerfunc  (r MyReader)   Read(p []byte ) (int , error ) {      return  r.Read(p) }
 
方法声明要与 receiver 参数的基类型声明放在同一个包内。 所以不能给原生类型和跨越go包给别的类型申明方法。 
receiver 参数的基类型为 T,那么 receiver 参数绑定在 T 上,可以通过 *T 或 T 的变量实例调用该方法:
1 2 3 4 5 6 7 8 9 10 11 12 type  T struct {}func  (t T)   M(n int ) { }func  main ()   {     var  t T     t.M(1 )      p := &T{}     p.M(2 )  }
 
方法本质 Go语言中的方法本质是,一个以方法的 receiver 参数作为第一个参数的普通函数 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func  Get (t T)   int  {       return  t.a  }func  Set (t *T, a int )   int  {      t.a = a      return  t.a  }var  t T t.Get() (&t).Set(1 )var  t T T.Get(t) (*T).Set(&t, 1 )
 
一个以方法的 receiver 参数作为第一个参数的普通函数。调用方式这种直接以类型名 T 调用方法的表达方式,被称为 Method Expression。 
Method Expression 有些类似于 C++ 中的静态方法(Static Method),C++ 中的静态方法在使用时,以该 C++ 类的某个对象实例作为第一个参数,而 Go 语言的 Method Expression 在使用时,同样以 receiver 参数所代表的类型实例作为第一个参数。
方法自身的类型就是一个普通函数的类型,甚至可以将它作为右值,赋值给一个函数类型的变量。
1 2 3 4 5 6 7 8 func  main ()   {     var  t T     f1 := (*T).Set      f2 := T.Get         f1(&t, 3 )      fmt.Println(f2(t))  }
 
如何选择receiver类型 1 2 func  (t T)   M1() <=> F1(t T)func  (t *T)   M2() <=> F2(t *T)
 
以 T 作为 receiver 参数类型时,M1 方法等价转换为 F1(t T),Go 函数的参数采用的是值拷贝传递,也就是说,F1 函数体中的 t 是 T 类型实例的一个副本。这样,我们在 F1 函数的实现中对参数 t 做任何修改,都只会影响副本,而不会影响到原 T 类型实例。
以 *T 作为 receiver 参数类型时,M2 方法等价转换为 F2(t *T)。传递给 F2 函数的 t 是 T 类型实例的地址, F2 函数体中对参数 t 做的任何修改,都会反映到原 T 类型实例上。
如果 Go 方法要把对 receiver 参数代表的类型实例的修改,反映到原类型实例上,那么应该选择 *T 作为 receiver 参数的类型。 
无论是 T 类型实例,还是 *T 类型实例,都既可以调用 receiver 为 T 类型的方法,也可以调用 receiver 为 *T 类型的方法,当 Go 编译器发现类型不一致后会自动转换。使用中可以根据是否允许该方法修改结构体实例的值来决定是用 *T 还是 T 作为 receiver,而不必担心调用方法时该怎么使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27  type  T struct  {      a int   }    func  (t T)   M1() {      t.a = 10   }func  (t *T)   M2() {     t.a = 11  }func  main ()   {     var  t1 T     println (t1.a)      t1.M1()     println (t1.a)      t1.M2()      println (t1.a)      var  t2 = &T{}     println (t2.a)      t2.M1()     println (t2.a)      t2.M2()      println (t2.a)  }
 
如果 receiver 参数类型的 size 较大,以值拷贝形式传入就会导致较大的性能开销,选择 *T 作为 receiver 类型可能更好些。
方法集合 Go 中任何一个类型都有属于自己的方法集合。方法集合是 Go 类型的一个“属性”。但不是所有类型都有自己的方法,比如 int 类型就没有。所以,对于没有定义方法的 Go 类型,称其拥有空方法集合。方法集合是用来判断一个类型是否实现了某接口类型的唯一手段。
Go 语言规定,*T 类型的方法集合包含所有以 *T 为 receiver 参数类型的方法,以及所有以 T 为 receiver 参数类型的方法。 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 type  Interface interface  {     M1()     M2() }type  T struct {}func  (t T)   M1()  {}func  (t *T)   M2() {}func  main ()   {     var  t T     var  pt *T     var  i Interface     i = pt     i = t  }
 
获取类型的方法集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 func  GetMethod (i interface {})   {     dynTyp := reflect.TypeOf(i)     if  dynTyp == nil  {         fmt.Printf("there is no dynamic type\n" )         return      }     n := dynTyp.NumMethod()     if  n == 0  {         fmt.Printf("%s's method set is empty!\n" , dynTyp)         return      }     fmt.Printf("%s's method set:\n" , dynTyp)     for  j := 0 ; j < n; j++ {         fmt.Println("-" , dynTyp.Method(j).Name)     }     fmt.Printf("\n" ) }func  main ()   { 	var  t T 	var  pt *T 	GetMethod(t) 	GetMethod(pt) }
 
输出结果:
1 2 3 4 5 6 main.T's method set: - M1 *main.T's method set: - M1 - M2