go interface

函数调用

值接收

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))
}

在这里插入图片描述

  1. 如果 interface 的动态类型是值的话,go runtime 会把值复制一份到堆上,并使 interface data 指向它
  2. 如果 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)
}

在这里插入图片描述

  1. 如果是值的话,go runtime 会把 interface data 指向的东西复制过来
  2. 如果是指针的话,go runtime 会让直接让指针指向 interface data 指向的东西
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值