函数调用
值接收
package main
import "fmt"
type Person struct {
age int
}
func (p Person) howOld() {
fmt.Println(p.age)
}
func (p Person) growUp() {
p.age += 1
}
值调用
func main() {
qcrao := Person{age: 18}
qcrao.howOld()
qcrao.growUp()
qcrao.howOld()
}
指针调用
func main() {
qcrao := &Person{age: 18}
qcrao.howOld()
qcrao.growUp()
qcrao.howOld()
}
指针接收
package main
import "fmt"
type Person struct {
age int
}
func (p *Person) howOld() {
fmt.Println(p.age)
}
func (p *Person) growUp() {
p.age += 1
}
值调用
func main() {
qcrao := Person{age: 18}
qcrao.howOld()
qcrao.growUp()
qcrao.howOld()
}
指针调用
func main() {
qcrao := &Person{age: 18}
qcrao.howOld()
qcrao.growUp()
qcrao.howOld()
}
实现接口
值实现接口
package main
import "fmt"
type coder interface {
code()
debug()
}
type Gopher struct {
language string
}
func (p Gopher) code() {
fmt.Printf("I am coding %s language\n", p.language)
}
func (p Gopher) debug() {
fmt.Printf("I am debuging %s language\n", p.language)
}
func main() {
var c coder = Gopher{"Go"}
c.code()
c.debug()
fmt.Printf("c: %T, %v\n", c, c)
c = &Gopher{"Go"}
c.code()
c.debug()
fmt.Printf("c: %T, %v\n", c, c)
}
表面上看, *Gopher
类型也没有实现 code,debug 方法,但是因为 Gopher 类型实现了 code,debug方法,所以让 *Gopher
类型自动拥有了 code,debug 方法。
对于接收者是值类型的方法,在方法中不会对接收者本身产生影响。所以,当实现了一个接收者是值类型的方法,就可以自动生成一个接收者是对应指针类型的方法,因为两者都不会影响接收者。
如果实现了接收者是值类型的方法,会隐含地也实现了接收者是指针类型的方法。
指针实现接口
package main
import "fmt"
type coder interface {
code()
debug()
}
type Gopher struct {
language string
}
func (p *Gopher) code() {
fmt.Printf("I am coding %s language\n", p.language)
}
func (p *Gopher) debug() {
fmt.Printf("I am debuging %s language\n", p.language)
}
func main() {
var c coder = Gopher{"Go"}
c.code()
c.debug()
fmt.Printf("c: %T, %v\n", c, c)
c = &Gopher{"Go"}
c.code()
c.debug()
fmt.Printf("c: %T, %v\n", c, c)
}
接收者是指针类型的方法,很可能在方法中会对接收者的属性进行更改操作,从而影响接收者。所以,当实现了一个接收者是指针类型的方法,如果此时自动生成一个接收者是值类型的方法,原本期望对接收者的改变(通过指针实现),现在无法实现,因为值类型会产生一个拷贝,不会真正影响调用者。
接口赋值
值或指针 => 接口
package main
import (
"fmt"
"unsafe"
)
type Animal interface {
howOld()
growUp()
}
type Person struct {
age int
}
func (p Person) howOld() {
fmt.Println(p.age)
}
func (p Person) growUp() {
p.age += 1
}
type iface struct {
itab, data uintptr
}
func main() {
var p1 Person = Person{18}
var i1 Animal = p1
fmt.Printf("i1 dyanmic type: %T\n", i1)
iface1 := *(*iface)(unsafe.Pointer(&i1))
fmt.Printf("p1 addr: %p, i1's data addr: %v\n", &p1, unsafe.Pointer(iface1.data))
var p2 *Person = &Person{18}
var i2 Animal = p2
fmt.Printf("i2 dyanmic type: %T\n", i2)
iface2 := *(*iface)(unsafe.Pointer(&i2))
fmt.Printf("p2 addr: %p, i2's data addr: %v\n", p2, unsafe.Pointer(iface2.data))
}
- 如果 interface 的动态类型是值的话,go runtime 会把值复制一份到堆上,并使 interface data 指向它
- 如果 interface 的动态类型是指针的话,go runtime 会让直接让 interface data 指向原指针指向的值
接口 => 接口
func main() {
var p1 Person = Person{18}
var i1 Animal = p1
var i3 Animal = i1
var i5 interface{} = i1
var i6 Animal = i5.(Animal)
fmt.Printf("i1 dyanmic type: %T, i3 dynamic type: %T, i5 dynamic type: %T, i6 dynamic type: %T\n", i1, i3, i5, i6)
iface1 := *(*iface)(unsafe.Pointer(&i1))
iface3 := *(*iface)(unsafe.Pointer(&i3))
iface5 := *(*iface)(unsafe.Pointer(&i5))
iface6 := *(*iface)(unsafe.Pointer(&i6))
fmt.Printf("p1 addr: %p, i1's data addr: %v, i3's data addr: %v, i5's data addr: %v, i6's data addr: %v\n", &p1, unsafe.Pointer(iface1.data), unsafe.Pointer(iface3.data), unsafe.Pointer(iface5.data), unsafe.Pointer(iface6.data))
var p2 *Person = &Person{18}
var i2 Animal = p2
var i4 Animal = i2
fmt.Printf("i2 dyanmic type: %T, i4 dyanmic type: %T\n", i2, i4)
iface2 := *(*iface)(unsafe.Pointer(&i2))
iface4 := *(*iface)(unsafe.Pointer(&i4))
fmt.Printf("p2 addr: %p, i2's data addr: %v, i4's data addr: %v\n", p2, unsafe.Pointer(iface2.data), unsafe.Pointer(iface4.data))
}
可以看出 接口 向 接口 赋值,会把 interface 对应的结构体,直接复制
type iface struct {
tab *itab
data unsafe.Pointer
}
接口 => 值或指针
func main() {
var p1 Person = Person{18}
var i1 Animal = p1
var i3 Animal = i1
var i5 interface{} = i1
var p3 Person = i1.(Person)
fmt.Printf("i1 dyanmic type: %T, i3 dynamic type: %T, i5 dynamic type: %T\n", i1, i3, i5)
iface1 := *(*iface)(unsafe.Pointer(&i1))
iface3 := *(*iface)(unsafe.Pointer(&i3))
iface5 := *(*iface)(unsafe.Pointer(&i5))
fmt.Printf("p1 addr: %p, i1's data addr: %v, i3's data addr: %v, i5's data addr: %v, p3 addr: %p\n", &p1, unsafe.Pointer(iface1.data), unsafe.Pointer(iface3.data), unsafe.Pointer(iface5.data), &p3)
var p2 *Person = &Person{18}
var i2 Animal = p2
var i4 Animal = i2
var p4 *Person = i2.(*Person)
fmt.Printf("i2 dyanmic type: %T, i4 dyanmic type: %T\n", i2, i4)
iface2 := *(*iface)(unsafe.Pointer(&i2))
iface4 := *(*iface)(unsafe.Pointer(&i4))
fmt.Printf("p2 addr: %p, i2's data addr: %v, i4's data addr: %v, p4 addr: %p\n", p2, unsafe.Pointer(iface2.data), unsafe.Pointer(iface4.data), p4)
}
- 如果是值的话,go runtime 会把 interface data 指向的东西复制过来
- 如果是指针的话,go runtime 会让直接让指针指向 interface data 指向的东西