参考:
https://blog.csdn.net/weixin_38138153/article/details/119254927
https://blog.csdn.net/qihoo_tech/article/details/104707905
一、规则
Golang方法集规则:
类型 | 方法参数接收类型 |
T | (t T) |
*T | (t Y) and (t *T) |
说明:即T类型,只能调用 接收类型是T的方法; *T类型,能调用 接收类型是T或者*T的方法;
★★★上述表格换种形式,如下(比较好理解):
方法参数接收类型 | 类型 |
(t T) | T and *T |
(t *T) | *T |
说明:即接收类型是T类型,那么T或者*T的变量可以调用该方法;接收类型是*T的类型,那么只能*T的变量可以调用该方法;
方法集总结(看完所有,再来体会下面5个规则):
• 类型 T 方法集包含全部 receiver T 方法。
• 类型 *T 方法集包含全部 receiver T + *T 方法。
• 如类型 S 包含匿名字段 T,则 S 和 *S 方法集包含 T 方法。
• 如类型 S 包含匿名字段 *T,则 S 和 *S 方法集包含 T + *T 方法。
• 不管嵌入 T 或 *T,*S 方法集总是包含 T + *T 方法。
二、普通类型的方法集
当类型调用自己申明的方法的时候,不需要考虑方法集,因为有语法糖帮忙;
package main import "fmt" type user struct { name string age int } // 指针类型 func (usr *user) sayHello() { fmt.Printf("user %s age is %d say hello\n", usr.name, usr.age) } // 结构体类型 func (usr user) sayHi() { fmt.Printf("user %s age is %d say hi\n", usr.name, usr.age) } func main() { u1 := user{name: "dog", age: 2} u1.sayHello() // 按照golang方法集规则,此处应该报错;原因是编译器在编译的时候为我们加上了取地址操作,为go语法糖 u1.sayHi() u2 := &user{name: "cat", age: 4} u2.sayHello() u2.sayHi() }
普通类型的嵌入方法:
package main import ( "fmt" ) type Person struct { name string age int } func (s Person) GetName() { fmt.Printf("this is %s\n", s.name) } func (s *Person) SetName(newName string) { s.name = newName } type Worker1 struct { Person } type Worker2 struct { *Person } func main() { s1 := Worker1{} s1.SetName("worker01") s1.GetName() s2 := &Worker1{} s2.SetName("worker2") s2.GetName() s3 := &Worker2{} s3.SetName("worker3") // 因为Person类型值为nil; runtime error: invalid memory address or nil pointer dereference s3.GetName() s4 := &Worker2{&Person{name: "work4", age: 20}} s4.SetName("worker04") s4.GetName() }
三、接口的方法集
普通接口方法,规则如下:
• 类型 T 方法集包含全部 receiver T 方法。
• 类型 *T 方法集包含全部 receiver T + *T 方法。
package main import "fmt" type personer interface { talk() eat() } type user struct { name string age int } func (usr user) talk() { fmt.Printf("user %s age is %d, talk\n", usr.name, usr.age) } func (usr *user) eat() { fmt.Printf("user %s age is %d, wat\n", usr.name, usr.age) } func main() { var x personer u := user{name: "dog", age: 2} x = &u x.talk() x.eat() x = u // 此处会报错;cannot use u (type user) as type personer in assignment: user does not implement personer (eat method has pointer receiver) x.talk() x.eat() }
说明:代码中为什么只有 *user 类型实现了person接口?首先,eat方法是用 *user作为参数的,这个当然算实现了eat方法,然后,talk方法是用 user作为参数的。根据上面的表格规则,当参数类型是T时,传T或者 *T 都是可以接受的,所以 *user 也实现了eat方法。反观user类型,eat方法中指定是 *user 类型,根据上面的表格,当参数是 *T 时,只能传 *T 所以user类型没有能访问到eat方法,所以它没有实现person接口。
嵌入接口方法,规则如下:
规则1: • 如类型 S 包含匿名字段 T,则 S 和 *S 方法集包含 T 方法。
规则2: • 如类型 S 包含匿名字段 *T,则 S 和 *S 方法集包含 T + *T 方法。
规则3: • 不管嵌入 T 或 *T,*S 方法集总是包含 T + *T 方法。
package main import ( "fmt" ) type Humaner interface { GetName() SetName(string) } type Person struct { name string age int } func (s Person) GetName() { fmt.Printf("this is %s\n", s.name) } func (s *Person) SetName(newName string) { s.name = newName } type Worker1 struct { Person } type Worker2 struct { *Person } func main() { // 根据规则三,不满足SetName方法,所以没有实现接口 var x1 Humaner = Worker1{} //此处报错;cannot use Worker1{} (type Worker1) as type Humaner in assignment: Worker1 does not implement Humaner (SetName method has pointer receiver) x1.SetName("worker01") x1.GetName() var x2 Humaner = &Worker1{} // ok,满足规则1、3 x2.SetName("worker02") x2.GetName() var x3 Humaner = Worker2{&Person{}} // ok,满足规则2 x3.SetName("worker03") x3.GetName() var x4 Humaner = &Worker2{&Person{}} // ok,满足规则2 x4.SetName("worker04") x4.GetName() }
.