6.7. 在接口和类型间转换¶
6.7.1. 1 类型断言的格式¶
接口类型I的变量 varI 中可以包含任何实现了这个接口的类型的值,如果多个类型都实现了这个接口,所以有时我们需要用一种动态方式来检测它的真实类型,即在运行时确定变量的实际类型。
通常我们可以使用类型断言(value, ok := element.(T))来测试在某个时刻接口变量 varI 是否包含类型 T 的值:
value, ok := varI.(T) // 类型断言
varI 必须是一个接口变量,否则编译器会报错:invalid type assertion:
varI.(T) (non-interface type (type of I) on left) 。
1.1 类型断言的例子¶
package main
import "fmt"
// 定义一个I接口,f()方法
type I interface {
f()
}
type T string
// 实现接口方法
func (t T) f() {
fmt.Println("T Meathod")
}
// 定义一个接口,一个方法,返回一个字符串
type Stringer interface {
String() string
}
func main() {
// 类型断言
var varI I
varI = T("Tstring")
if v, ok := varI.(T); ok {
// 类型断言
fmt.Println("varI 类型断言结果为:", v) // varI已经转为T类型 //varI 类型断言结果为: Tstring
varI.f() //T Meathod
}
//Type-switch做类型判断
var value interface{} // 默认为0值
switch str := value.(type) {
case string:
fmt.Println("value类型的断言结果为string: ", str)
case Stringer:
fmt.Println("value类型断言结果为Stringer:", str)
default:
fmt.Println("value 类型不在上述类型之中....") //value 类型不在上述类型之中....
}
// Comma-ok断言
value = "类型断言检查"
str, ok := value.(string)
if ok {
fmt.Printf("value类型断言结果为:%T\n", str) // value类型断言结果为:string
} else {
fmt.Printf("value不是string类型 \n")
}
}
6.7.2. 2.将接口转换为其他接口¶
实现某个接口的类型同时实现了另外一个接口,此时可以在两个接口间转换。
鸟和猪具有不同的特性,鸟可以飞,猪不能飞,但两种动物都可以行走。
如果使用结构体实现鸟和猪,让它们具备自己特性的 Fly() 和 Walk() 方法就让鸟和猪各自实现了飞行动物接口(Flyer)和行走动物接口(Walker)。
将鸟和猪的实例创建后,被保存到 interface{} 类型的 map 中。
interface{} 类型表示空接口,意思就是这种接口可以保存为任意类型。
对保存有鸟或猪的实例的 interface{} 变量进行断言操作,如果断言对象是断言指定的类型,则返回转换为断言对象类型的接口;
如果不是指定的断言类型时,断言的第二个参数将返回 false。
例如下面代码
var obj interface = new(bird)
f, isFlyer := obj.(Flyer)
package main
import "fmt"
// 定义飞行动物的接口
type Falyer interface {
Fly()
}
// 定义行走动物的接口
type Walker interface {
Walk()
}
// 定义鸟类
type bird struct {
}
// 为鸟添加Walk()方法,实现行走动物接口。
func (b *bird) Walk() {
fmt.Println("bird: walk")
}
// 定义猪
type pig struct {
}
func (p *pig) Walk() {
fmt.Println("pig: walk")
}
func main() {
// 创建动物的名字到实例的映射
animals := map[string]interface{}{
"bird": new(bird),
"pig": new(pig),
}
// 遍历映射
for name, object := range animals {
// 判断对象是否为飞行动物
f, isFlyer := object.(Falyer)
// 判断对象是否为行走动物
w, isWalker := object.(Walker)
fmt.Printf("name: %s isFlay: %v isWalker: %v\n", name, isFlyer, isWalker)
// 如果是飞行动物则调用飞行动物接口
//根据飞行动物和行走动物两者是否断言成功,调用其接口。
if isFlyer {
f.Fly()
}
// 如果是行走动物则调用行走动物接口
if isWalker {
w.Walk()
}
}
}
/*
name: bird isFlay: false isWalker: true
bird: walk
name: pig isFlay: false isWalker: true
pig: walk
*/
6.7.3. 3.将接口转换为其他类型¶
// 实例化pig类
p1 := new(pig)
// 将类关联到接口
var a Walker = p1
p2 := a.(*pig)
fmt.Printf("p1=%p p2=%p\n", p1, p2) //p1=0x597c18 p2=0x597c18
由于pig实现了Walker接口,因此可以被隐式转换为Walker接口类型,保存于a中。
a中保存的本来就是
*pig本体,因此可以转换为*pig类型p1和p2的指针是相同的。