Go方法集

参考:

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()
}

.

原文地址:https://www.cnblogs.com/wsongl/p/15748977.html