其实是一个很简单的问题,但是如果是之前一直写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)
}