2.07 Go之接口和类型之间的转换
什么是接口和类型之间的转换?
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。
因此,接口在转换为其他类型时,接口内保存的实例对应的类型指针,必须是要转换的对应的类型指针。
*/
    It's a lonely road!!!
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号