Golang:使用reflect探究struct成员方法接收者指针

来源:互联网 发布:墨眉剑淘宝 编辑:程序博客网 时间:2024/06/14 17:03

问题背景

Go语言的面向对象在概念上与java大同小异,但是由于Go语言在给struct添加method的时候需有一个显示的接收者(receiver),receiver可以是指针类型或是struct的形参,二者到底有啥区别是在学习Go面向对象内容时最容易糊涂的地方。


问题描述

为了更好的阐述问题,首先撸上一段小代码:

//定义一个名为Controller的类type Controller struct {    domain string    count  int}//给Controller添加第一个成员方法firstFuncfunc (c *Controller) firstFunc(domain string, count int) {    c.domain = domain    c.count = count    fmt.Println("firstFunc's domain is "+c.domain+" count is ", c.count)}//给Controller添加第二个成员方法secondFuncfunc (c Controller) secondFunc(domain string, count int) {    c.domain = domain    c.count = count    fmt.Println("secondFunc's domain is "+c.domain+" count is ", c.count)}

如上,在Go中定义类用struct类型,在Controller类有两个field,分别为domiancount,还添加了两个方法,分别是firstFunc(domain string, count int)secondFunc(domain string, count int),不同的是firstFunc的receiver是Controller的指针而secondFunc传入的receiver是Controller的形参。那么问题来了不同类型的receiver具体会有哪些区别呢?下面将以笔者拙见进行总结归纳,分享之余也用于给自己记录。


问题解答

  • 对类字段值修改结果不同
    这个问题根据指针的意义就能理解,receiver为指针时相当于类的引用,*Controller.domain=domain修改了Controller中domain字段的值,receiver为形参时,相当于Controller类的一个copy值,因此Controller.domain=domain只是修改了Controller的一个副本的字段值,并非原Controller本身字段值。代码验证如下:
func main() {    //创建一个名为controller的Controller实体指针并初始化字段值    controller := &Controller{        domain: "www.baidu.com",        count:  1000,    }    //输出controller原始字段值    fmt.Println("conroller's  original domain is <"+controller.domain+"> count is ", controller.count)    //调用firstFunc并查看controller字段值    controller.firstFunc("www.sohu.com", 500)    fmt.Println("firstFunc>conroller's domain is <"+controller.domain+"> count is ", controller.count)    //调用secondFunc并查看controller字段值    controller.secondFunc("www.qq.com", 800)    fmt.Println("secondFunc>conroller's domain is <"+controller.domain+"> count is ", controller.count)}

运行结果如下:
运行结果


  • Controller实例与Controller实例指针包含方法不同
    简单概括来讲,类的实例指针(也就是上一段代码中的controller)包含所有方法,即在以上代码中controller包含firstFunc和secondFunc两个方法,而实例只包含receiver为形参的方法,即若controller不是指针则firstFunc是不能够被成功调用的,因为其receiver为指针。为了能够更加直观的进行说明,下面用reflect反射机制进行验证:
func main() {    //初始化一个类指针controller1    controller1 := &Controller{        domain: "www.baidu.com",        count:  1000,    }    //初始化一个类controller2    controller2 := Controller{        domain: "www.sohu.com",        count:  800,    }    t1 := reflect.TypeOf(controller1)    //查看controller1的类型与包含的方法个数    fmt.Println("controller1's type is", t1.Kind(), "include", t1.NumMethod(), "methods")    //查看controller1的所有方法名称    for j := 0; j < t1.NumMethod(); j++ {        fmt.Println(++j,t1.Method(j).Name)    }    //查看controller2的类型与包含的方法个数    fmt.Println("controller2's type is", t2.Kind(), "include", t2.NumMethod(), "methods")    //查看controller2的所有方法名称    for j := 0; j < t2.NumMethod(); j++ {        fmt.Println(t2.Method(j).Name)    }}

执行结果如下:
结果


  • 对接口实现的区别
    接口通常会规定一些方法,实现了这些方法的类就相当于实现了相应接口,类方法的receiver是否为指针对接口实现的影响主要还在于类是否包含接口规定的所有方法。现定义一个接口:
type ControllerInter interface {    firstFunc(domain string, count int)    secondFunc(domain string, count int)}

在上一问题验证代码中controller1因为包含了ControllerInter规定的所有方法,因此实现了该接口,而controller则没有实现。因此在判断类是否实现了某一接口时要特别注意其成员方法的receiver类型以及类的类型,尽管结果已经很清晰,但同样还是用代码加以验证:

func main() {    controller1 := &Controller{        domain: "www.baidu.com",        count:  1000,    }    controller2 := Controller{        domain: "www.sohu.com",        count:  800,    }    var controllerInter ControllerInter    controllerInter = controller1    controllerInter = controller2}

执行结果如下:
这里写图片描述
声明一个名为controllerInter的接口变量,并将controller1和controller2一次复制给它,实现了接口的将顺利执行,没有实现的将会报错,如上图中controller2 does not implement ControllerInter接口。


结语

本文为作者原创,如有错误还请指正,轻拍!

1 0
原创粉丝点击