GO系列(2)-interface特性

一. interface与struct的调用区别

struct特性:

说明struct,在调用方法的时候,值类型既可以调用值接收者的方法,也可以调用指针接收者的方法;指针类型既可以调用指针接收者的方法,也可以调用值接收者的方法

interface不行,必须严格按照特性,比如

例子一. 不管方法的接收者是什么类型,该类型的值和指针都可以调用,不必严格符合接收者的类型。

package main

import "fmt"

type Person struct {
    age int
}

func (p Person) howOld() int {
    return p.age
}

func (p *Person) growUp() {
    p.age += 1
}

func main() {
    // qcrao 是值类型
    qcrao := Person{age: 18}

    // 值类型 调用接收者也是值类型的方法
    fmt.Println(qcrao.howOld())

    // 值类型 调用接收者是指针类型的方法
    qcrao.growUp()
    fmt.Println(qcrao.howOld())

    // ----------------------

    // stefno 是指针类型
    stefno := &Person{age: 100}

    // 指针类型 调用接收者是值类型的方法
    fmt.Println(stefno.howOld())

    // 指针类型 调用接收者也是指针类型的方法
    stefno.growUp()
    fmt.Println(stefno.howOld())
}

输出结果:

18
19
100
101

|- |值接收者 |指针接收者 |

|:--|

|值类型调用者 |方法会使用调用者的一个副本,类似于“传值” |使用值的引用来调用方法,上例中,qcrao.growUp() 实际上是 (&qcrao).growUp() |

|指针类型调用者 |指针被解引用为值,上例中,stefno.howOld() 实际上是 (*stefno).howOld() |实际上也是“传值”,方法里的操作会影响到调用者,类似于指针传参,拷贝了一份指针 |

例子二. 如果实现了接收者是值类型的方法,会隐含地也实现了接收者是指针类型的方法

当实现了一个接收者是指针类型的方法,如果此时自动生成一个接收者是值类型的方法,原本期望对接收者的改变(通过指针实现),现在无法实现,因为值类型会产生一个拷贝,不会真正影响调用者

通俗的话理解:

在下面的例子中func (p *Gopher) debug()这种实现有了之后,不会自动生成func (p Gopher) debug(),但是如果实现了func (p Gopher) code(),会隐式实现func (p *Gopher) code(), 因为自动生成的话会影响调用者。

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 pointer\n", p.language)
}

func main() {
    var c coder = &Gopher{"Go"}
    c.code()
    c.debug()

    var gopher Gopher = Gopher{"Go"}
    gopher.code()
    /*下面两者等同*/
    (&gopher).debug()
    gopher.debug()
}

注意23行,如果不写&,那么会出现错误

image.png

二. iface 和 eface

1. 两者区别是什么

iface 和 eface 都是 Go 中描述接口的底层结构体,区别在于 iface 描述的接口包含方法,而 eface 则是不包含任何方法的空接口:interface{}

查看源码文件

/usr/local/Cellar/go@1.17/1.17.10/libexec/src/runtime/runtime2.go

type iface struct {
   tab  *itab
   data unsafe.Pointer
}

type eface struct {
   _type *_type
   data  unsafe.Pointer
}

其中iface中的itab是指向

type itab struct {
    inter *interfacetype
    _type *_type
    hash  uint32 // copy of _type.hash. Used for type switches.
    _     [4]byte
    fun   [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}

_type 字段描述了实体的类型,包括内存对齐方式,大小等;

inter 字段则描述了接口的类型。

fun 字段放置和接口方法对应的具体数据类型的方法地址,实现接口调用方法的动态分派,一般在每次给接口赋值发生转换时会更新此表,或者直接拿缓存的 itab。

image.png

2. 查看汇编代码

go tool compile -S test03.go

image.png

三. 接口判空以及打印

1. 接口值的零值是指动态类型和动态值都为 nil。当仅且当这两部分的值都为 nil 的情况下,这个接口值就才会被认为 接口值 == nil

package main

import "fmt"

type Coder interface {
   code()
}

type Gopher struct {
   name string
}

func (g Gopher) code() {
   fmt.Printf("%s is coding\n", g.name)
}

func main() {
   var c Coder
   fmt.Println(c == nil)
   fmt.Printf("c: %T, %v\n", c, c)
   var g *Gopher
   fmt.Println(g == nil)
   fmt.Printf("g: %T, %v\n", g, g)

   fmt.Println("====================")

   /*接口值的零值是指动态类型和动态值都为 nil。当仅且当这两部分的值都为 nil 的情况下,这个接口值就才会被认为 接口值 == nil*/
   c = g
   fmt.Println(c == nil)
   fmt.Printf("c: %T, %v\n", c, c)
}

输出结果

true
c: <nil>, <nil>
true
g: *main.Gopher, <nil>
====================
false
c: *main.Gopher, <nil>

2. 结构题隐式转换的判断

package main

import "fmt"

type MyError struct {}

func (i MyError) Error() string {
    return "MyError"
}

func main() {
    err := Process()
    fmt.Println(err)

    fmt.Println(err == nil)
}

func Process() error {
    var err *MyError = nil
    return err
}

打印结果

<nil>
false

3. 如何打印出接口的动态类型和值?

package main

import (
   "fmt"
   "unsafe"
)

type iface struct {
   itab, data uintptr
}

func main() {
   var a interface{} = nil

   var b interface{} = (*int)(nil)

   x := 5
   var c interface{} = (*int)(&amp;x)

   ia := *(*iface)(unsafe.Pointer(&amp;a))
   ib := *(*iface)(unsafe.Pointer(&amp;b))
   ic := *(*iface)(unsafe.Pointer(&amp;c))

   fmt.Println(ia, ib, ic)

   fmt.Println(*(*int)(unsafe.Pointer(ic.data)))
}

输出结果

{0 0} {17361600 0} {17361600 824634322600}
5

4. 内置方法结构体的隐式调用转换会失效!

package main

import "fmt"

type Student struct {
   Name string
   Age int
}

func (s *Student) String() string {
   return fmt.Sprintf("[Name: %s], [Age: %d]", s.Name, s.Age)
}

func (p *Student) debug() {
   fmt.Printf("I am debuging language pointer\n")
}

func main() {
   var s Student = Student{
      Name: "qcrao",
      Age: 18,
   }
   s.debug()
   fmt.Println(s)
}

这里我们看到,String()没有被调用到.对于内置类型,函数内部会用穷举法,得出它的真实类型,然后转换为字符串打印。而对于自定义类型,首先确定该类型是否实现了 String() 方法,如果实现了,则直接打印输出 String() 方法的结果;否则,会通过反射来遍历对象的成员进行打印。

输出结果

I am debuging language pointer
{qcrao 18}

参考文档

  1. 两万字深入解密 Go 语言接口的那些事儿 | 技术头条

原文地址:https://cloud.tencent.com/developer/article/2137435

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340