首先,本篇不是为了讲值接收和指针接收的概念,因为概念本身很好理解,不过为了后面的验证还是先写一个例子,简单说明一下。
type treeNode struct {
value int
left, right *treeNode
}
func (node *treeNode) setValue(val int) {
node.value = val
}
func (node treeNode) getValue() int{
return node.value
}
例子很简单,定义了一个树节点的结构体,给出了一个设置树节点值的方法,和一个获取树节点值的方法。
显然,setValue是指针接收,getValue是值接收。
作为一个初学者,我想搞清楚的是,指针接收者可以接收值吗?值接收者又可以接收指针吗?
想搞清楚这个问题,最好的办法就是自己验证,两个问题,一个一个说。
首先,指针接收者接收指针肯定是没有问题的。
func main() {
root := new(treeNode)
root.setValue(1)
fmt.Println(root.value)
}
结果:
改成接收值,再试一次。
func main() {
//root := new(treeNode)
root := treeNode{0, nil, nil}
root.setValue(1)
fmt.Println(root.value)
}
结果:
也是没有问题的,也就是说,指针接收者即使接收的是值,也会先将其转化为对应的指针,再做处理。
同样的思路,首先,值接收者接收值肯定是没问题的。
func main() {
root := treeNode{0, nil, nil}
fmt.Println(root.getValue())
}
结果:
改成接收指针,再试一次。
func main() {
root := new(treeNode)
fmt.Println(root.getValue())
}
结果:
也是没有问题的,也就是说,值接收者即使接收到指针,也会先取出值,再做处理。
值/指针接收者均可以接收指针/值。
为什么是结论1?因为到这里还没完,上面我们只是定义了结构体,并没有用这个结构体实现接口。
如果是接口的话,条件并没有这么宽松。
我们首先写一个接口。
type Stack interface {
Push(val int)
Pop() int
}
然后定义一个结构体,并且定义Stack接口的两个方法。
type Mystack []int
func (stack *Mystack) Pop() int {
tail := (*stack)[len(*stack)-1]
*stack = (*stack)[:len(*stack)-1]
return tail
}
func (stack *Mystack) Push(val int) {
*stack = append(*stack, val)
}
这样,Mystack结构体就实现了Stack接口。接下来用一下试试。
先把接口赋值为结构体指针。
func main() {
var stack Stack
stack = &mystack.Mystack{1}
fmt.Println(stack)
stack.Push(4)
fmt.Println(stack)
stack.Pop()
fmt.Println(stack)
}
结果:
这个没有问题,那再试试赋值结构体值。
结果,还没运行goland就报错了:
这意思就是,Mystack这个结构体的Push方法是接收指针的,赋值成值是不行的。由此观之,当结构体方法接收指针时,接口也必须赋值为结构体指针。
接下来我又把Mystack的两个方法改成了接收值类型,然后接口也赋值为值。
func (stack Mystack) Pop() int {
tail := stack[len(stack)-1]
stack = stack[:len(stack)-1]
return tail
}
func (stack Mystack) Push(val int) {
stack = append(stack, val)
}
stack = mystack.Mystack{1}
这时候编译运行就没有问题了,结果如下:
那么问题又来了,当结构体的方法都是接收值时,接口能不能赋值为指针呢?
修改代码:
stack = &mystack.Mystack{1}
然后再运行,结果如下:
看来是欧克的,下面总结一下。
如果结构体实现了接口,那么:
如果结构体的方法中有接收指针的,接口赋值时必须是结构体指针;
如果结构体的方法都是接收值的,接口可以赋值为结构体值,也可以赋值为结构体指针。