### 方法
- Go 语言中的方法的本质就是，一个以方法的 receiver 参数作为第一个参数的普通函数
![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/methods.jpg)
- 方法支持赋值给变量，相当于第一个参数是结构体自身或者自身的指针类型
```go
package main

import "fmt"

type T struct{}

func (t *T) M(n int) {
	fmt.Println(n)
}

func (t T)N(n int) {
	fmt.Println(n)
}

func main() {
	m1 := (*T).M
	m1(&T{}, 2)

	m2 := T.N
	m2(T{}, 3)
}
```

### 方法接收receiver
```go
func (t *T或T) MethodName(参数列表) (返回值列表) {
    // 方法体
}
```
- T为基类型
- receiver 参数的基类型本身不能为指针类型或接口类型
- 每个方法只能有一个 receiver 参数
- Go 不支持在方法的 receiver 部分放置包含多个 receiver 参数的参数列表，或者变长 receiver 参数
- 方法接收器（receiver）参数、函数 / 方法参数，以及返回值变量对应的作用域范围，都是函数 / 方法体对应的显式代码块
- receiver 部分的参数名不能与方法参数列表中的形参名，以及具名返回值中的变量名存在冲突，必须在这个方法的作用域中具有唯一性
- 如果在方法体中，我们没有用到 receiver 参数，我们也可以省略 receiver 的参数名

### 方法声明约束
- 方法声明要与 receiver 参数的基类型声明放在同一个包内
- 不能为原生类型（诸如 int、float64、map 等）添加方法
- 不能跨越 Go 包为其他包的类型声明新方法

### 选择receiver 原则
- 如果需要修改receiver内部属性，使用指针类型
- 不需要修改内部属性，使用结构体类型，传参值拷贝，但是结构体过大时，选用指针类型
- 如果 T 类型需要实现某个接口，那就要使用 T 作为 receiver 参数的类型，来满足接口类型方法集合中的所有方法

### 方法集合
```go
- 用来判断一个类型是否实现了某接口类型的唯一手段
- T类型实现了M1方法
- *T类型实现了M1和M2方法
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 // cannot use t (type T) as type Interface in assignment: T does not implement Interface (M2 method has pointer receiver)
}
```

### type定义的新类型和命名的类型的方法集合
- 自定义非接口类型的 defined 类型的方法集合为空
- 自定义接口类型的 defined 类型的方法集合为接口的方法
```go
package main

type T struct{}

func (T) M1()  {}
func (*T) M2() {}

type T1 T

func main() {
  var t T
  var pt *T
  var t1 T1
  var pt1 *T1

  dumpMethodSet(t)
  dumpMethodSet(t1)

  dumpMethodSet(pt)
  dumpMethodSet(pt1)
}


main.T's method set:
- M1

main.T1's method set is empty!

*main.T's method set:
- M1
- M2

*main.T1's method set is empty!
```
- 无论原类型是接口类型还是非接口类型，类型别名都与原类型拥有完全相同的方法集合
```go

type T struct{}

func (T) M1()  {}
func (*T) M2() {}

type T1 = T

func main() {
    var t T
    var pt *T
    var t1 T1
    var pt1 *T1

    dumpMethodSet(t)
    dumpMethodSet(t1)

    dumpMethodSet(pt)
    dumpMethodSet(pt1)
}


main.T's method set:
- M1

main.T's method set:
- M1

*main.T's method set:
- M1
- M2

*main.T's method set:
- M1
- M2
```