• 2.07 Go之接口和类型之间的转换


    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。

    因此,接口在转换为其他类型时,接口内保存的实例对应的类型指针,必须是要转换的对应的类型指针。
    */

     

  • 相关阅读:
    JS自动微信消息轰炸
    会议管家——常用的JQ知识点
    关于微信分享
    前端——解决微信网页清除缓存的方法
    2018新知识点
    一键分享代码(提供能分享到QQ空间、新浪微博、人人网等的分享功能)
    如何在网中使用百度地图API自定义个性化地图
    spring5 源码深度解析----- IOC 之 默认标签解析(上)
    spring5 源码深度解析----- IOC 之 容器的基本实现
    高级Java工程师必备 ----- 深入分析 Java IO (三)
  • 原文地址:https://www.cnblogs.com/JunkingBoy/p/15944288.html
Copyright © 2020-2023  润新知