美文网首页Golang进阶
【Golang】接口、方法、指针、自定义类型-摸清他们的千丝万缕

【Golang】接口、方法、指针、自定义类型-摸清他们的千丝万缕

作者: qishuai | 来源:发表于2017-11-26 08:50 被阅读46次

    主要区分一下两个方面的内容:

    • 单纯的方法定义
    • 通过接口传递参数

    1、 单纯的方法定义

    go语言内部会自动进行值和指针的转换, 代码在编译的时候不会出错;区别在于使用指针定义方法,方法操作的是该数据本身;而使用值定义方法时,方法操作的是该数据的拷贝。

    总结:如果使用除接口类型以外的类型作为接收者时,使用值和指针调用方法不会出现编译错误; 如果使用接口类型的变量(实现了该接口)调用方法时,使用值调用指针定义的方法时会出现编译出错。

    1、 使用值定义方法,使用值调用方法的情况

    type user struct {
        name string
        email string
    }
    
    func (u user) notify() {
        //将传入的参数复制一份,赋值给u
        u.name = "Jack"
        fmt.Println("Send email to", u.name, u.email)
    }
    
    func main() {
        user := user{"Andy", "1139329@163.com"}
        user.notify()   
        fmt.Println(user)
    }
    
    输出(名子并不会改变):
    Send email to Jack 1139329@163.com
    {Andy 1139329@163.com}
    

    2、 使用值定义方法,使用指针调用方法的情况

    由于定义方法时使用的是值,在编译过程中会对调用者为指针的类型进行解引用,内部实现为 *user.notify()

    type user struct {
        name string
        email string
    }
    
    func (u user) notify() {
        //将传入的参数复制一份,赋值给u
        u.name = "Jack"
        fmt.Println("Send email to", u.name, u.email)
    }
    
    func main() {
        user := &user{"Andy", "1139329@163.com"}
        user.notify()   
        fmt.Println(user)
    }
    
    输出(名子也不会改变):
    Send email to Jack 1139329@163.com
    {Andy 1139329@163.com}
    

    3、 使用指针定义方法,使用指针调用方法的情况

    type user struct {
        name string
        email string
    }
    
    func (u *user) notify() {
        u.name = "Jack"
        fmt.Println("Send email to", u.name, u.email)
    }
    
    func main() {
        user := user{"Andy", "1139329@163.com"}
        user.notify()
        fmt.Println(user)
    }
    
    输出(名子会改变):
    Send email to Jack 1139329@163.com
    {Jack 1139329@163.com}
    

    4、 使用指针定义方法,使用值调用方法的情况

    内部实现为 *user.notify()

    type user struct {
        name string
        email string
    }
    
    func (u *user) notify() {
        u.name = "Jack"
        fmt.Println("Send email to", u.name, u.email)
    }
    
    func main() {
        user := user{"Andy", "1139329@163.com"}
        user.notify()
        fmt.Println(user)
    }
    
    输出(名子会改变):
    Send email to Jack 1139329@163.com
    {Jack 1139329@163.com}
    

    2、 通过接口传递参数

    1、 接受者receiver为值,使用值传递的情况

    type user struct {
        name  string
        email string
    }
    
    type notifyInterface interface {
        notify()
    }
    
    func (u user) notify() {
        fmt.Println("Send email to", u.name, u.email)
    }
    
    func sendNotification(n notifyInterface) {
        n.notify()
    }
    
    func main() {
        user := user{"Andy", "1139329@163.com"}
        sendNotification(user)
    }
    
    //编译成功
    

    2、 接受者receiver为值,使用指针传递的情况

    type user struct {
        name  string
        email string
    }
    
    type notifyInterface interface {
        notify()
    }
    
    func (u user) notify() {
        fmt.Println("Send email to", u.name, u.email)
    }
    
    func sendNotification(n notifyInterface) {
        n.notify()
    }
    
    func main() {
        user := &user{"Andy", "1139329@163.com"}
        sendNotification(user)
    }
    
    //编译成功
    

    3、 接受者receiver为指针,使用指针传递的情况

    type user struct {
        name  string
        email string
    }
    
    type notifyInterface interface {
        notify()
    }
    
    func (u *user) notify() {
        fmt.Println("Send email to", u.name, u.email)
    }
    
    func sendNotification(n notifyInterface) {
        n.notify()
    }
    
    func main() {
        user := &user{"Andy", "1139329@163.com"}
        sendNotification(user)
    }
    
    //编译成功
    

    4、 接受者receiver为指针,使用值传递的情况

    type user struct {
        name  string
        email string
    }
    
    type notifyInterface interface {
        notify()
    }
    
    func (u *user) notify() {
        fmt.Println("Send email to", u.name, u.email)
    }
    
    func sendNotification(n notifyInterface) {
        n.notify()
    }
    
    func main() {
        user := user{"Andy", "1139329@163.com"}
        sendNotification(user)
    }
    
    //编译失败(使用指针接受者来实现一个接口,值类型无法实现对应的接口)
        cannot use user (type user) as type notifyInterface in argument to sendNotification:
        user does not implement notifyInterface (notify method has pointer receiver)
    

    针对以上情况,《Go语言实战》一书中这样讲到,首先这是Go语言的一种规则,具体如下:如果使用指针接受者来实现一个接口,那么只有指向那个类型的指针才能够实现对应的接口。如果使用值接受者来实现一个接口,那么那个类型的值和指针都能够实现对应的接口。

    为什么会有这样的限制呢:作者解释为go编译器并不总能自动获得一个值得地址!

    相关文章

      网友评论

      • dc989982faa5:您好,我想问一下为什么这句代码没问题:a := map[string]*data {"x": {"one"}}
        {“one”}是个值呀,是什么机制让它可以直接赋值给指针呢?
        qishuai:main.go
        ```
        package main

        type data struct {
        A string
        }

        func main() {
        _ = map[string]*data{"x": &data{"one"}}
        }
        ```

        使用gofmt工具,简化代码书写:
        `gofmt -s -w main.go`

        结果就是:
        `_ = map[string]*data{"x": {"one"}}`

      本文标题:【Golang】接口、方法、指针、自定义类型-摸清他们的千丝万缕

      本文链接:https://www.haomeiwen.com/subject/mptkbxtx.html