golang 方法
golang语言中的方法是与对象实例绑定的特殊函数,用于维护和展示对象的自身状态。 与函数的区别是方法有前置实例接收参数(receiver),编译器根据receiver来判断该方法属于哪个实例。receiver可以是基础类型,也可以是指针类型,这会关系到是否需要有可以修改对象实例的能力。 在调用方法时,可以使用对象实例值或指针,编译器会根据receiver类型自动在基础类型和指针类型之间转换,比如: typerectstruct{ width,height,areaint } func(r*rect)pointer(){ r.width+=2 r.area=r.width*r.height } func(rrect)value(){ r.width+=4 r.area=r.width*r.height } funcmain(){ r:=rect{width:10,height:5} r.value() fmt.Println(r) r.pointer() fmt.Println(r) /* r:=&rect{width:10,height:5} r.value() fmt.Println(r) r.pointer() fmt.Println(r) */ } 输出: {1050} {12560} 如果使用指针调用方法时,需要注意不能使用多级指针,且必须使用合法的指针(包括nil)或能取实例地址,比如: typeXstruct{} func(x*X)test(){ fmt.Println("hellogopher") } funcmain(){ varx*X fmt.Println(x) x.test() &X{}.test()//cannottaketheaddressofXliteral } 如何选择方法的receiver类型?
通过匿名字段的方法访问: 可以像访问匿名字段成员那样调用其方法,由编译器负责查找,比如: typepersonstruct{} typeManstruct{ person } func(pperson)toWork()string{ return"Tom" } funcmain(){ varmMan fmt.Println(m.toWork()) } 输出: Tom 如果Man结构体也有个同名的toWork方法,此时调用逻辑如下,比如: typepersonstruct{} typeManstruct{ person } func(pperson)toWork()string{ return"Tomtowork" } func(mMan)toWork()string{ return"metowork" } funcmain(){ varmMan fmt.Println(m.toWork())//metowork fmt.Println(m.person.toWork())//Tomtowork } 方法集: GoLang规范中提到了一个与类型相关的方法集(method set),这决定了它是否实现了某个接口。
typeSstruct{} typeTstruct{ S } func(S)SVal(){} func(*S)SPtr(){} func(T)TVal(){} func(*T)TPtr(){} funcmethodSet(ainterface{}){ t:=reflect.TypeOf(a) fori,n:=0,t.NumMethod();i<n;i++{ m:=t.Method(i) fmt.Println(m.Name,m.Type) } } funcmain(){ vartT methodSet(t) println("--------------") methodSet(&t) } 输出: SValfunc(main.T) TValfunc(main.T) -------------- SPtrfunc(*main.T) SValfunc(*main.T) TPtrfunc(*main.T) TValfunc(*main.T) 很显然, 匿名字段就是为扩展方法集准备的。否则没有必要少写个字段。这种组合没有父子依赖关系, 整体与局部松耦合,可以任意增加来实现扩展。各单元互无关联,实现与维护更加简单。 方法表达式: 方法是一种特殊的函数,除了可以直接调用之外,还可以进行赋值或当作参数传递,下面是Go语言的方法定义格式,比如: func(pmytype)funcname(qtype)(r,stype){return0,0} 本质上这就是一种语法糖,方法调用如下: instance.method(args)->(type).func(instance,args) instance 就是Reciever,左边的称为Method Value;右边则是Method Expression,Go推荐使用左边形式。 Method Value是包装后的状态对象,总是与特定的对象实例关联在一起(类似闭包), 而Method Expression会被还原成普通的函数,将Receiver作为第一个显式参数,调用时需额外传递。 二者本质上没有区别,只是Method Value 看起来更像面向对象的格式,且编译器会自动进行类型转换; Method Expression更直观,更底层,编译器不会进行类型转换,会按照实际表达意义去执行,更易于理解。 Method Expression: typePersonstruct{ Ageint Namestring } func(pPerson)GetAge()int{ returnp.Age } func(p*Person)SetAge(iint){ p.Age=i } funcmain(){ p:=Person{20,"Tom"} setAge:=(*Person).SetAge//(*Person)必须用括号,整体相当于func(p*Person) setAge(&p,50)//编译器不会进行类型转换 getAge:=Person.GetAge fmt.Println(getAge(p)) } 输出: 50 Method Value: funcmain(){ p:=Person{20,"Tom"} setAge:=p.SetAge//编译器会自动进行类型转换 setAge(50) getAge:=p.GetAge fmt.Println(getAge()) } 只要receiver参数类型正确,使用nil同样可以执行,比如: typeNint func(nN)value(){ println(n) } func(n*N)pointer(){ println(n) } funcmain(){ varn*N n.pointer() (*N)(nil).pointer() (*N).pointer(nil) } 这样写程序并没有什么意义,只是希望你能理解并安全使用Method Value和Method Expression (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |