Ezreal 书架 Ezreal 书架
Home
  • 《Go程序员面试笔试宝典》
  • 《RabbitMQ 实战指南》
  • 《深入理解kafka》
  • MySQL45讲
  • 透视HTTP协议
  • 结构化数据的分布式存储系统
  • Raft 共识算法
Home
  • 《Go程序员面试笔试宝典》
  • 《RabbitMQ 实战指南》
  • 《深入理解kafka》
  • MySQL45讲
  • 透视HTTP协议
  • 结构化数据的分布式存储系统
  • Raft 共识算法
  • 逃逸分析

  • 延迟语句

  • 数据容器

  • 通道

  • 接口

    • Go 接口与 C++接口有何异同
    • Go 语言与“鸭子类型”的关系
    • iface 和 eface 的区别是什么
    • 值接收者和指针接收者的区别
    • 如何用 interface 实现多态
    • 接口的动态类型和动态值是什么
    • 接口转换的原理是什么
    • 类型转换和断言的区别是什么
    • 如何让编译器自动检测类型是否实现了接口
  • unsafe

  • context

  • Go程序员面试笔试宝典
  • 接口
ezreal_rao
2023-05-31

接口的动态类型和动态值是什么

回顾一下前面的内容,iface 包含两个字段:tab 是接口表指针,指向类型信息;data 是数据指针,则指向具体的数据,它们分别被称为动态类型和动态值,而接口值则包括动态类型和动态值。

我们来看一个接口类型和 nil 做比较的例子,当仅且当动态类型和动态值这两部分的值都为 nil 的情况下,接口值 == nil 为 true:

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)
	c = g
	fmt.Println(c == nil)
	fmt.Printf("c: %T, %v\n", c, c)

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

输出如下:

true
c: <nil>, <nil>
true
false
c: *main.Gopher, <nil>
1
2
3
4
5

例子中,c 是一个接口,g 则是一个指针。一开始,c 的动态类型和动态值都为 nil,g 的值也为 nil,当把 g 赋值给 c 后,c 的动态类型变成了 *main.Gopher 尽管 c 的动态值仍为 nil,但是当 c 和 nil 做比较的时候,结果就是 false 了。

来看一个隐含的类型转换的例子:

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
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

函数运行结果:

<nil>
false
1
2

这里先定义了一个 MyError 结构体,实现了 Error 函数,也就实现了 error 接口。Process 函数返回了一个 error 接口,这里隐含了类型转换,即 MyError 类型转换成 error 接口。所以,虽然它的动态值是 nil,但是它的动态类型是 *MyError,最后和 nil 比较的时候,结果为 false。

如何打印出接口的动态类型和值?下面的代码给出了一个例子:

package main

import (
  "unsafe"
  "fmt"
)

type iface struct {
  itab, data uintptr
}

func main() {
  var a interface{} = nil
  var b interface{} = (*int)(nil)
  x := 5
  var c interface{} = (*int)(&x)
  ia := *(*iface)(unsafe.Pointer(&a))
  ib := *(*iface)(unsafe.Pointer(&b))
  ic := *(*iface)(unsafe.Pointer(&c))
  fmt.Println(ia, ib, ic)
  fmt.Println(*(*int)(unsafe.Pointer(ic.data)))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

代码里直接定义了一个 iface 结构体,用两个指针来描述 itab 和 data,之后将 a, b, c 在内存中的内容强制解释成自定义的 iface。最后就可以打印出动态类型和动态值的地址。

运行结果如下:

{0 0} {17426912 0} {17426912 842350714568}
5
1
2

可见,a 的动态类型和动态值的地址均为 0,也就是 nil;b 的动态类型和 c 的动态类型一致,都是 *int;最后,c 的动态值为 5。

#golang#interface
上次更新: 7/12/2024, 2:37:05 PM
如何用 interface 实现多态
接口转换的原理是什么

← 如何用 interface 实现多态 接口转换的原理是什么→

最近更新
01
为什么我的MySQL会抖一下
07-15
02
HTTP 性能优化面面观
07-12
03
WebSocket:沙盒里的 TCP
07-12
更多文章>
Theme by Vdoing | Copyright © 2022-2024 Ezreal Rao | CC BY-NC-SA 4.0
豫ICP备2023001810号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式