golang函数参数中接口指针的传递
其实是一个很简单的问题,但是如果是之前一直写go的话可能没有意识到指针的本质,就走不出来了。
最近写代码的时候遇到了一个问题:有一个功能需要使用一个接口,有多个结构体实现了这个接口(经典OO场景)。这些方法中,有一些方法可以修改结构体中的指定属性,并且有一个对应的方法来返回这个属性。
出于业务需要,值被修改的地方和它被使用的地方是不同的。由于要和原有代码兼容,希望这个代码尽量表现的与原来的一样。
一个小demo,直接返回interface值来完成传递。看着很正常,但是因为是传值,所以与原有代码不太一致,也不够直观。
package main
import "fmt"
type tt interface {
setName(string)
getName() string
}
type testA struct{
name string
}
type testDouble struct{
name string
}
func (t *testA) setName(n string){
t.name = n
}
func (t *testA) getName() string{
return t.name
}
func (t *testDouble) setName(n string){
t.name = n + n
}
func (t *testDouble) getName() string{
return t.name
}
func setName(s tt,n string) tt{
t := testA{}
t.setName("test")
s = &t
s.setName(n)
return s
}
func main(){
var s tt
s = setName(s,"tset2")
fmt.Println(s.getName())
/*
//origin code need get Name after set
var a testA
setsetName(&a,"test")
fmt.Println(a.getName())
*/
}
但如果试图使用接口直接作为函数参数的时候,会报错
func setName(s *tt,n string) tt{
//t := testA{}
t := testDouble{}
t.setName("test")
s = &t
s.setName(n)
return s
}
func main(){
var s tt
s = setName(&s,"tset2")
fmt.Println(s.getName())
}
在s=&t
的地方会报错:Cannot use '&t' (type *testDouble) as type *tt
,非指针的情况下会报错Cannot use 't' (type testDouble) as type *tt
这里比较让人迷惑的地方在于,interface tt = testDouble
是很容易成立的(编译器支持),可是指针层面却并不像想象中这样继续支持,强制转换也是不行的。这个应该是与golang的底层实现相关了,现在暂时没空拆。
这个问题的实际实现上倒也不难想,直接绕回去即可
//s所对应的内容设置完成之后应该能够返回
func setName(s *tt,n string){
//t := testA{}
//s里面的内容应该与t是相同的
t := testDouble{}
t.setName("test")
//对应的接口变量
var regular tt = &t
regular.setName(n)
*s = regular//完成转换
fmt.Println(regular.getName())
fmt.Println(s,*s)
}
func main(){
var s tt
setName(&s,"tset2")
fmt.Println(&s,s)
}