代码改变世界

golang 类型和类型断言

2022-03-19 22:12  youxin  阅读(481)  评论(0编辑  收藏  举报

类型断言即判断一个变量是不是某个类型的实例,这个经常用在判断接口的类型,基本的格式:

 
y, ok := x.(type)
  上面的语句用于判断变量x是不是type类型,有两种结果:

x是type类型的变量,那么返回x的副本赋值给y,ok的值为true
x不是type类型的变量,那么返回一个空的stuct,ok的值为false
  注意判断x是不是type类型的变量时,那么 type类型的结构(struct) 就必须实现 x类型的接口,否则进行类型断言是不合法的

 

接口类型断言

func main() {

    var i interface{} = "Hello"
    s := i.(string)
    fmt.Println(s)

    s, ok := i.(string)
    fmt.Println(s, ok)

    f, ok := i.(float64)
    fmt.Println(f, ok)

    f = i.(float64) // 报错(panic)
    fmt.Println(f)

}
接口类型选择


func do(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Twice %v is %v\n", v, v*2)
    case string:
        fmt.Printf("%q is %v bytes long\n", v, len(v))
    default:
        fmt.Printf("I don't know about type %T!\n", v)
    }
}
func main() {
    do(21)
    do("hello")
    do(true)
}
 
 
 

golang中的类型

golang中的有两种类型,静态类型(static type)动态类型(dynamic type)

静态类型:静态类型是在声明变量的时候指定的类型,一个变量的静态类型是永恒不变的,所以才被称为静态类型,也可以简称为类型,普通变量只有静态类型。

package main

import "fmt"

func main()  {
    // 变量i和变量g只有静态类型,变量i的类型为int,i的类型为main.Goose,
    var i int
    var g Goose
    fmt.Printf("%T\n", i)
    fmt.Printf("%T\n", g)
}

type Goose struct {
    age  int
    name string
}

执行结果:
int
main.Sparrow

动态类型:接口类型变量除了静态类型之外还有动态类型,动态类型是由给接口类型变量赋值的具体值的类型来决定的,除了动态类型之外还有动态值,动态类型和动态值是对应的,动态值就是接口类型变量赋值的具体值,之所以被称为动态类型,是因为接口类型的动态类型是会变化的,由被赋予的值来决定。

package main
import "fmt"
func main()  {
    // 动态类型
    var b Bird
    // b 是main.Sparrow类型
    b = Sparrow{}
    fmt.Printf("%T\n", b)
    // b 是main.Parrot类型
    b = Parrot{}
    fmt.Printf("%T\n", b)
}
type Bird interface {
    fly()
    sing()
}
// Goose implement Bird interface
type Sparrow struct {
    age  int
    name string
}
func (s Sparrow) fly()  {
    fmt.Println("I am flying.")
}
func (s Sparrow) sing()  {
    fmt.Println("I can sing.")
}
type Parrot struct {
    age  int
    kind int
    name string
}
func (p Parrot) fly()  {
    fmt.Println("I am flying.")
}
func (p Parrot) sing()  {
    fmt.Println("I can sing.")
}

执行结果:
main.Sparrow
main.Parrot

但是我们在变量b赋值之前直接获取它的类型会发现返回的结果是nil,这看起来很奇怪,interface在Golang里也是一种类型,那它声明的变量的类型为什么是nil呢?

package main
import "fmt"
func main()  {
    // 动态类型
    var b Bird
    fmt.Printf("interface类型变量的类型:%T\n", b)
}
type Bird interface {
    fly()
    sing()
}

执行结果:
interface类型变量的类型:<nil>

首先我们需要明确,一个接口类型变量在没有被赋值之前,它的动态类型和动态值都是 nil 。在使用 fmt.Printf("%T\n") 获取一个变量的类型时,其实是调用了reflect包的方法进行获取的, reflect.TypeOf 获取的是接口变量的动态类型, reflect.valueOf() 获取的是接口变量的动态值。所以 fmt.Printf("%T\n",b) 展示的是 reflect.TypeOf 的结果,由于接口变量 b 还没有被赋值,所以它的动态类型是 nil ,动态值也会是 nil 。

对比来看,为什么只是经过了声明未赋值的变量的类型不是 nil 呢?就像在静态类型部分中所展示的那样。原因如下: 我们先来看一下 reflect.TypeOf 函数的定义,func TypeOf(i interface{}) Type{} ,函数的参数是一个 interface 类型的变量,在调用 TypeOf 时,在接口变量 b 没有赋值之前,它的静态类型与参数类型一致,不需要做转换,因为 b 的动态类型为 nil,所以 TypeOf 返回的结果为 nil 。那为什么变量 i 和变量 g 的类型不为 nil 呢?当变量 i 调用 TypeOf 时,会进行类型的转换,将int型变量i转换为 interface 型,在这个过程中会将变量 i 的类型作为 b 的动态类型,变量 i 的值(在这里是变量 i 的零值0)作为 b 的动态值。因为 TypeOf() 获取的是变量 b 的动态类型,所以这个时候展示出的类型为 int

golang中的类型断言

因为接口变量的动态类型是变化的,有时我们需要知道一个接口变量的动态类型究竟是什么,这就需要使用类型断言,断言就是对接口变量的类型进行检查,其语法结构如下:

value, ok := x.(T)
x表示要断言的接口变量;
T表示要断言的目标类型;
value表示断言成功之后目标类型变量;
ok表示断言的结果,是一个bool型变量,true表示断言成功,false表示失败,如果失败value的值为nil。

代码示例如下:

package main

import (
    "fmt"
)

func main() {
    // 动态类型
    var b Bird
    // b 是main.Sparrow类型
    b = Sparrow{}

    Listen(b)

    // b 是main.Parrot类型
    b = Parrot{}

    Listen(b)
}


func Listen(b Bird) {
    if _, ok := b.(Sparrow); ok {
        fmt.Println("Listren sparrow sing.")
    }

    if _, ok := b.(Parrot); ok {
        fmt.Println("Listren parrot sing.")
    }

    b.sing()
}

type Bird interface {
    fly()
    sing()
}

type Sparrow struct {
    age  int
    name string
}

func (s Sparrow) fly() {
    fmt.Println("I am sparrow, i can fly.")
}
func (s Sparrow) sing() {
    fmt.Println("I am sparrow, i can sing.")
}

type Parrot struct {
    age  int
    kind int
    name string
}

func (p Parrot) fly() {
    fmt.Println("I am parrot, i can fly.")
}
func (p Parrot) sing() {
    fmt.Println("I am parrot, i can sing.")
}

执行结果如下:
Listren sparrow sing.
I am sparrow, i can sing.
Listren parrot sing.
I am parrot, i can sing.

有时结合 switch 使用更方便

func Listen(b Bird) {
    switch b.(type) {
    case Sparrow:
        fmt.Println("Listren sparrow sing.")
    case Parrot:
        fmt.Println("Listren parrot sing.")
    default:
        fmt.Println("Who are you? I don't know you.")
    }
    b.sing()
}

参考:

https://studygolang.com/articles/9331

https://studygolang.com/articles/11374

《go语言圣经》