golang 方法

前端之家收集整理的这篇文章主要介绍了golang 方法前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

golang语言中的方法是与对象实例绑定的特殊函数,用于维护和展示对象的自身状态。

函数的区别是方法有前置实例接收参数(receiver),编译器根据receiver来判断该方法属于哪个实例。receiver可以是基础类型,也可以是指针类型,这会关系到是否需要有可以修改对象实例的能力。

调用方法时,可以使用对象实例值或指针,编译器会根据receiver类型自动在基础类型和指针类型之间转换,比如:

  1. typerectstruct{
  2. width,height,areaint
  3. }
  4.  
  5. func(r*rect)pointer(){
  6. r.width+=2
  7. r.area=r.width*r.height
  8. }
  9. func(rrect)value(){
  10. r.width+=4
  11. r.area=r.width*r.height
  12. }
  13.  
  14. funcmain(){
  15. r:=rect{width:10,height:5}
  16. r.value()
  17. fmt.Println(r)
  18. r.pointer()
  19. fmt.Println(r)
  20. /*
  21. r:=&rect{width:10,height:5}
  22. r.value()
  23. fmt.Println(r)
  24. r.pointer()
  25. fmt.Println(r)
  26. */
  27. }
  28. 输出
  29. {1050}
  30. {12560}

如果使用指针调用方法时,需要注意不能使用多级指针,且必须使用合法的指针(包括nil)或能取实例地址,比如:

  1. typeXstruct{}
  2.  
  3. func(x*X)test(){
  4. fmt.Println("hellogopher")
  5. }
  6.  
  7. funcmain(){
  8. varx*X
  9. fmt.Println(x)
  10. x.test()
  11. &X{}.test()//cannottaketheaddressofXliteral
  12. }


如何选择方法的receiver类型?

- 如果要修改实例状态,用*T

- 如果不需要修改实例状态的小对象或固定值,建议用T

- 如果是大对象,建议用*T,可以减少复制成本

- 引用类型、字符串、函数等指针包装对象,用T

- 如果对象实例中包含Mutex等同步字段,用*T,以免因复制造成锁无效

- 无法确定需求的情况,都用*T

通过匿名字段的方法访问:

可以像访问匿名字段成员那样调用方法,由编译器负责查找,比如:

  1. typepersonstruct{}
  2.  
  3. typeManstruct{
  4. person
  5. }
  6. func(pperson)toWork()string{
  7. return"Tom"
  8. }
  9. funcmain(){
  10. varmMan
  11. fmt.Println(m.toWork())
  12. }
  13. 输出
  14. Tom

如果Man结构体也有个同名的toWork方法,此时调用逻辑如下,比如:

  1. typepersonstruct{}
  2.  
  3. typeManstruct{
  4. person
  5. }
  6.  
  7. func(pperson)toWork()string{
  8. return"Tomtowork"
  9. }
  10.  
  11. func(mMan)toWork()string{
  12. return"metowork"
  13. }
  14. funcmain(){
  15. varmMan
  16. fmt.Println(m.toWork())//metowork
  17. fmt.Println(m.person.toWork())//Tomtowork
  18. }

方法集:

GoLang规范中提到了一个与类型相关的方法集(method set),这决定了它是否实现了某个接口。

- 类型T方法集包含所有receiver T方法

- 类型*T方法集包含所有receiver T + *T的方法

- 匿名嵌入S,T方法集包含所有receiver S方法

- 匿名嵌入*S,T方法集包含所有receiver S + receiver *S方法

- 匿名嵌入*S或匿名嵌入S,*T方法集包含所有receiver S + receiver *S方法

  1. typeSstruct{}
  2.  
  3. typeTstruct{
  4. S
  5. }
  6.  
  7. func(S)SVal(){}
  8. func(*S)SPtr(){}
  9. func(T)TVal(){}
  10. func(*T)TPtr(){}
  11.  
  12. funcmethodSet(ainterface{}){
  13. t:=reflect.TypeOf(a)
  14. fori,n:=0,t.NumMethod();i<n;i++{
  15. m:=t.Method(i)
  16. fmt.Println(m.Name,m.Type)
  17. }
  18. }
  19.  
  20. funcmain(){
  21. vartT
  22. methodSet(t)
  23. println("--------------")
  24. methodSet(&t)
  25. }
  26. 输出
  27. SValfunc(main.T)
  28. TValfunc(main.T)
  29. --------------
  30. SPtrfunc(*main.T)
  31. SValfunc(*main.T)
  32. TPtrfunc(*main.T)
  33. TValfunc(*main.T)

很显然, 匿名字段就是为扩展方法集准备的。否则没有必要少写个字段。这种组合没有父子依赖关系,

整体与局部松耦合,可以任意增加来实现扩展。各单元互无关联,实现与维护更加简单。


方法表达式:

方法是一种特殊的函数,除了可以直接调用之外,还可以进行赋值或当作参数传递,下面是Go语言的方法定义格式,比如:

  1. func(pmytype)funcname(qtype)(r,stype){return0,0}

本质上这就是一种语法糖,方法调用如下:

  1. 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:

  1. typePersonstruct{
  2. Ageint
  3. Namestring
  4. }
  5.  
  6. func(pPerson)GetAge()int{
  7. returnp.Age
  8. }
  9.  
  10. func(p*Person)SetAge(iint){
  11. p.Age=i
  12. }
  13.  
  14. funcmain(){
  15. p:=Person{20,"Tom"}
  16. setAge:=(*Person).SetAge//(\*Person)必须用括号,整体相当于func(p*Person)
  17. setAge(&p,50)//编译器不会进行类型转换
  18. getAge:=Person.GetAge
  19. fmt.Println(getAge(p))
  20. }
  21. 输出
  22. 50

Method Value:

  1. funcmain(){
  2. p:=Person{20,"Tom"}
  3. setAge:=p.SetAge//编译器会自动进行类型转换
  4. setAge(50)
  5. getAge:=p.GetAge
  6. fmt.Println(getAge())
  7. }

只要receiver参数类型正确,使用nil同样可以执行,比如:

  1. typeNint
  2.  
  3. func(nN)value(){
  4. println(n)
  5. }
  6. func(n*N)pointer(){
  7. println(n)
  8. }
  9.  
  10. funcmain(){
  11. varn*N
  12. n.pointer()
  13. (*N)(nil).pointer()
  14. (*N).pointer(nil)
  15. }

这样写程序并没有什么意义,只是希望你能理解并安全使用Method Value和Method Expression

猜你在找的Go相关文章