5

Golang中的类型和类型断言

 1 year ago
source link: https://studygolang.com/articles/20041?fr=sidebar
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Golang中的类型和类型断言

LeeYubo · 2019-04-26 17:14:08 · 8268 次点击 · 预计阅读时间 3 分钟 · 大约8小时之前 开始浏览    
这是一个创建于 2019-04-26 17:14:08 的文章,其中的信息可能已经有所发展或是发生改变。

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语言圣经》


有疑问加站长微信联系(非本文作者))

280

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK