通过一个题目三种变式讲清楚go接口类型断言
【第一种】一种类型实现多个接口,各个接口变量调用各自的方法
type Work struct {
i int
}
func (w Work) ShowA() int {
return w.i + 10
}
func (w Work) ShowB() int {
return w.i + 20
}
func main() {
c := Work{3}
var a A = c
var b B = c
fmt.Println(a.ShowA())
fmt.Println(b.ShowB())
}
输出:13 23。
一种类型实现多个接口,结构体 Work 分别实现了接口 A、B,所以接口变量 a、b 调用各自的方法 ShowA() 和 ShowB(),输出 13、23。
【第二种】一种类型实现多个接口,各个接口变量调用不是自己的方法
type Work struct {
i int
}
func (w Work) ShowA() int {
return w.i + 10
}
func (w Work) ShowB() int {
return w.i + 20
}
func main() {
c := Work{3}
var a A = c
var b B = c
fmt.Println(a.ShowB())
fmt.Println(b.ShowA())
}
a、b 具有相同的动态类型和动态值,分别是结构体 work 和 {3};a 的静态类型是 A,b 的静态类型是 B,接口 A 不包括方法 ShowB(),接口 B 也不包括方法 ShowA(),编译报错。
【第三种】一种类型实现多个接口,各个接口变量进行类型转换后,调用不是自己的方法
type A interface {
ShowA() int
}
type B interface {
ShowB() int
}
type Work struct {
i int
}
func (w Work) ShowA() int {
return w.i + 10
}
func (w Work) ShowB() int {
return w.i + 20
}
func main() {
var a A = Work{3}
s := a.(Work)
fmt.Println(s.ShowA())
fmt.Println(s.ShowB())
}
输出:13 23。
通过类型断言将接口变量 a 转换为具体类型 Work。
调用 Work 类型的方法 ShowA 和 ShowB,并根据 w.i 的值计算结果。
底层原理
这涉及到了 Go 语言中的接口和类型断言的底层原理。
-
接口的内部表示:
- 在 Go 中,接口的内部表示包括两部分:值和类型指针。
- 当一个类型被赋值给一个接口变量时,会将这个值的副本存储在接口的值部分,同时将指向类型信息的指针存储在接口的类型部分。
-
类型断言:
- 类型断言允许我们从接口中检索其动态类型的值。
- 当我们使用类型断言将接口值转换为具体类型时,Go 会检查接口的动态类型和我们所期望的类型是否匹配。
- 如果匹配,类型断言会返回具体类型的值;如果不匹配,则会导致运行时恐慌。
在这个例子中,虽然变量 a
是一个 A
类型的接口变量,但它包含的是一个 Work
类型的值,所以它实际上拥有 Work
类型的方法集。因此,当我们将 a
进行类型断言,转换为 Work
类型的值 s
时,s
实际上是一个 Work
类型的变量,它可以调用 Work
类型的所有方法,包括 ShowB()
。
这就是为什么在这个例子中,尽管 a
是一个 A
类型的接口变量,但我们仍然可以调用 s.ShowB()
而不会报错。因为在运行时,s
是一个 Work
类型的变量,而 Work
类型有 ShowB()
方法。