Golang 方法

Golang 中,方法是一种作用于特定类型变量的函数,这种特定类型变量叫做:接收者( Receiver )

  • ( 了解 )也就是说,自定义类型,都可以有方法,而不仅仅是结构体
  •  方法与函数的区别是,函数不属于任何类型,方法属于特定的类型,即:接收者
// 基本语法
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
    函数体
}

// 接收者变量:接收者变量,在命名时,官方建议使用接收者类型名称首字母的小写,而不是self、this之类的命名
             // 如,Person类型的接收者变量,应该命名为 p,Connector类型的接收者变量,应该命名为 c
// 接收者类型:接收者类型和参数类似,可以是指针类型、非指针类型
// 方法名、参数列表、返回参数:具体格式与函数定义相同

为结构体添加方法  &  方法的调用

!important  _  使用值类型 / 指针类型,作为函数参数( 方法接收者 )的区别

So,什么时候应该使用指针类型接收者 ?

  • 需要修改原数据中的值
  • 接收者,是拷贝代价比较大的对象
  • 保持一致性,如果某个方法使用了指针接收者,那么,其他的方法,也应该使用指针接收者

type Person struct {
	name string
	age  int
}

// 为结构体添加方法

// 值类型的接收者 — 方法内的修改,不会对原对象产生影响
func (receiver Person) say1(sth string) {
	fmt.Println(sth) // hello
	// 方法接收者是值类型
	receiver.age = 21
}

// 指针类型的接收者 — 方法内的修改,会对原对象产生影响
// 需说明的是,是否对原数据产生影响,与方法的调用者无关,而是取决于方法接收者的类型
func (receiver *Person) say2(sth string) {
	fmt.Println(sth) // world
	receiver.age = 24
}

func main() {
	p := Person{"小明", 20}

	// 方法的调用:方式 1( 常用 )

	p.say1("hello") // 值类型的接收者
	fmt.Println(p)  // {小明 20}

	(&p).say2("world") // 指针类型的接收者
	fmt.Println(p)     // {小明 24}

	// 方法的调用:方式2( 拓展 )— 方法值和方法表达式( 了解 )

	// 方法值
	f1 := p.say2
	f1("方法值")      // 打印  "方法值"
	fmt.Println(p) // {小明 24}

	// 方法表达式
	f2 := (*Person).say2 // 接收者类型.方法名  // 需注意的是,接收者类型,需要与方法的接收者类型一致
	f2(&p, "方法表达式")      // 参数,需要指定方法接收者
	// 或
	f3 := Person.say1
	f3(p, "方法表达式")

}

另,拓展 — 方法的调用:方法值和方法表达式( 了解 )


方法的继承与重写

  • Golang 中,没有方法重载,也就是说:不同在一个包中,写名称相同的方法,即使参数不同
  • Golang 中,支持方法重写,也就是说:方法,是可以继承的( 方法的接收者不同 ),如果匿名字段实现了一个方法,那么,包含这个匿名字段的子结构体,也能调用该方法,甚至对该方法进行重写

换句话来说,就是:Golang 中,不允许在同一个包中,写相同的方法

  • 方法名相同,即使参数不同,也视为相同的方法
  • 方法接收者不同,视为不同的方法
// 父类
type Person struct {
	name string
	age  int
}

// 子类
type Student struct {
	Person
	school string
}

// 父类方法
func (p *Person) walk() {
	fmt.Println("Person walk")
}

// 父类方法
func (s *Person) run() {
	fmt.Println("Person run")
}

// 子类重写父类方法 - 调用者不同,即使方法名相同,也视为不同的方法
func (s *Student) run() {
	fmt.Println("Student run")
}

func main() {
	p := Person{"Tom", 20}
	fmt.Println(p) // {Tom 20}
	p.walk()       // Person walk

	s := Student{Person{"Jerry", 18}, "xxx学校"}
	fmt.Println(s) // {{Jerry 18} xxx学校}
	// (1)方法的继承:子类方法可以继承父类方法
	s.walk() // Person walk

	p.run() // Person run
	// (2)方法重写:子类方法名,与父类方法名相同,优先调用子类方法
	s.run() // Student run
	// 如果子类想调用父类方法 ...
	s.Person.run() // Person run
}