什么是接口和类型之间的转换?
Go语言中使用接口断言(type assertions
)将接口转换成另外一个接口,也可以将接口转换为另外的类型。
类型断言的格式
格式:
i.(T)
。 i
表示一个接口的类型和T
表示一个类型
断言的基本格式:
t := i.(T)
/*
i 代表接口变量,T 代表转换的目标类型,t 代表转换后的变量
*/
三种情况:
-
断言的类型
T
是一个具体类型,类型断言检查i
的动态类型是否和T
相同。如果检查成功,类型断言结果是i
的动态值,类型是T
。如果检查失败则会抛出panic
var w io.Writer
w = os.Stdout
f := w.(*os.File) // 成功: f == os.Stdout
c := w.(*bytes.Buffer) // 死机:接口保存*os.file,而不是*bytes.buffer
-
断言的类型
T
是一个接口类型,类型断言检查是否i
的动态类型满足T
,如果这个检查成功了,动态值没有获取到;这个结果仍然是一个有相同类型和值部分的接口值,但是结果有类型T
var w io.Writer
w = os.Stdout
rw := w.(io.ReadWriter) // 成功:*os.file具有读写功能
w = new(ByteCounter)
rw = w.(io.ReadWriter) // 死机:*字节计数器没有读取方法
/*
第一个类型断言后,w 和 rw 都持有 os.Stdout 因此它们每个有一个动态类型 *os.File,但是变量 w 是一个 io.Writer 类型只对外公开出文件的 Write 方法,然而 rw 变量也只公开它的 Read 方法。
*/
-
断言对象是一个
nil
接口值,不论断言类似是什么这个类型断言都会失败
如果i
没有完全实现T
接口的方法,这个语句将会触发宕机。可以写成这样:
t,ok := i.(T)
/*
如果发生接口未实现时,将会把 ok 置为 false,t 置为 T 类型的 0 值。
正常实现时,ok 为 true。这里 ok 可以被认为是:i 接口是否实现 T 类型的结果。
*/
将接口转换为其他接口
概念:
一个类型同时实现了两个接口,可以在这两个接口之间切换
示例代码:
package main
import "fmt"
/* 定义飞行动物接口 */
type Flyer interface {
Fly()
}
/* 定义行走动物接口 */
type Walker interface {
Walk()
}
/* 定义鸟类型 */
type bird struct {
}
/* 定义鸟类型的方法 */
func (b *bird) Fly() {
fmt.Println("bird behavior : fly")
}
func (b *bird) Walk() {
fmt.Println("bird behavior : walk")
}
/* 定义类型 */
type pig struct {
}
/* 定义类型的方法 */
func (p *pig) Walk() {
fmt.Println("pig behavior : walk")
}
// 调用上述方法,使用类型断言
func main() {
// 创建动物的名字到实例的映射
animals := map[string]interface{}{
"bird" : new(bird),
"pig" : new(pig),
}
// 遍历map
for name, obj := range animals {
// 使用类型断言判断值得类型
f, isFlyer := obj.(Flyer)
w, isWalker := obj.(Walker)
// 打印结果
fmt.Printf("name: %s isFlyer: %v isWalker: %v\n", name, isFlyer, isWalker)
// 判断结果
if isFlyer {
f.Fly()
}
if isWalker {
w.Walk()
}
}
}
/*
最终结果bird调用了Fly()和Walk()方法,因为bird类型实现了两个方法
*/
将接口转换为其他类型
代码示例:
// 由于 pig 实现了 Walker 接口,因此可以被隐式转换为 Walker 接口类型保存于 a 中
p1 := new(pig)
var a Walker = p1
// 由于 a 中保存的本来就是 *pig 本体,因此可以转换为 *pig 类型
p2 := a.(*pig)
// p1 和 p2 指针是相同的
fmt.Printf("p1=%p p2=%p", p1, p2)
注意:
如果将*pig
转换为*bird
类型将会报错。
panic: interface conversion: main.Walker is *main.pig, not *main.bird
/*
接口转换时,main.Walker 接口的内部保存的是 *main.pig,而不是 *main.bird。
因此,接口在转换为其他类型时,接口内保存的实例对应的类型指针,必须是要转换的对应的类型指针。
*/