零基础 go - 57(类型断言)

5 阅读4分钟

由于接口是一般类型,如果转为具体的类型,就需要类型断言

场景案例

  • 定义一个 USB 接口

  • 定义一个 Phone 结构体,Phone 有额外的属性和方法,但也实现了 USB 接口的方法

  • 定义一个 Computer 结构体,Computer 也实现了 USB 接口的方法

  • 让 Phone 和 Computer 都实现 USB 接口

  • 定义一个函数 UseUsb,接收 USB 接口类型的参数,并调用接口的方法

  • 在 UseUsb 函数中,使用类型断言来访问具体类型的方法或属性(如果不用类型断言访问,会编译报错)


package main

import "fmt"

type USB interface {

    Connect()

}

type Phone struct {

    Name string

}

func (p Phone) Connect() {

    fmt.Printf("%s connected\n", p.Name)

}

type Computer struct {

    Brand string

}

func (c Computer) Connect() {

    fmt.Printf("%s computer connected\n", c.Brand)

}

func UseUsb(u USB) {

    u.Connect()

  


    // 没有使用断言,无法访问具体类型的方法或属性

    // fmt.Printf("这是Phone 变量的Name属性:%s\n", u.Name) // 编译报错

    // fmt.Printf("这是Phone 变量的Name属性:%s\n", u.Brand) // 编译报错




    // 使用类型断言来访问具体类型的方法或属性

    fmt.Printf("这是Phone 变量的Name属性:%s\n", u.(Phone).Name) // 断言为 Phone 类型

    fmt.Printf("这是Computer 变量的Name属性:%s\n", u.(Computer).Brand) // 断言为 Computer 类型




    // 使用 switch 语句和类型断言来处理不同的具体类型

    switch v := u.(type) {

        case Phone:

            fmt.Printf("This is a phone named %s\n", v.Name)

        case Computer:

            fmt.Printf("This is a computer named %s\n", v.Brand)

        default:

            fmt.Println("Unknown USB device")

    }




    // 类型断言带上检查机制

    phone, ok := u.(Phone);

    if ok {

        fmt.Printf("This is a phone named %s\n", phone.Name)

    }

    if computer, ok := u.(Computer); ok {

        fmt.Printf("This is a computer named %s\n", computer.Brand)

    }

}

func main() {

    phone := Phone{Name: "iPhone"}

    computer := Computer{Brand: "Dell"}

    UseUsb(phone)

    UseUsb(computer)

}

类型断言

  • 语法:变量名.(类型)

var x interface{} = "hello"

str := x.(string) // 断言 x 是 string 类型,并将其赋值给 str 变量

  • 类型断言检查机制

var x interface{} = "hello"

str, ok := x.(string) // 断言 x 是 string 类型,并将结果赋值给 str 和 ok 变量

if ok {

    fmt.Printf("x is a string: %s\n", str)

} else {

    fmt.Println("x is not a string")

}

  • 类型断言在 switch 语句中的使用

var x interface{} = 123

switch v := x.(type) {

    case int:

        fmt.Printf("x is an integer: %d\n", v)

    case string:

        fmt.Printf("x is a string: %s\n", v)

    default:

        fmt.Println("x is of unknown type")

}

  • 类型断言在接口类型转换中的使用

type Reader interface {

    Read(p []byte) (n int, err error)

}

type File struct {

    Name string

}

func (f File) Read(p []byte) (n int, err error) {

    // 实现 Reader 接口的 Read 方法

    return 0, nil

}

func main() {

    var r Reader = File{Name: "data.txt"}

    // 将 Reader 接口类型断言为 File 类型,以访问 File 的 Name 属性

    file := r.(File)

    fmt.Printf("Reading from file: %s\n", file.Name)

}

类型断言的注意事项

  • 如果断言失败(即变量的实际类型与断言的类型不匹配),会导致运行时 panic。因此,在进行类型断言时,建议使用带检查机制的方式,以避免程序崩溃。

var x interface{} = 123

str := x.(string) // 断言失败,x 实际上是 int 类型,会导致 panic

str, ok := x.(string) // 带检查机制的断言,避免 panic

if ok {

    fmt.Printf("x is a string: %s\n", str)

} else {

    fmt.Println("x is not a string")

}

  • 类型断言只能用于接口类型的变量,因为只有接口类型的变量才具有动态类型信息。对于非接口类型的变量,编译器会报错.

var x int = 10

str := x.(string) // 编译报错,因为 x 不是接口类型,无法进行类型断言

类型断言的应用场景

  • 当你需要访问接口变量的具体类型的方法或属性时,可以使用类型断言来实现。

type USB interface {

    Connect()

}

type Phone struct {

    Name string

}

func (p Phone) Connect() {

    fmt.Printf("%s connected\n", p.Name)

}

var u USB = Phone{Name: "iPhone"}

// 需要访问 Phone 类型的 Name 属性

phone := u.(Phone) // 断言 u 是 Phone 类型

  • 当你需要根据接口变量的具体类型执行不同的逻辑时,可以使用类型断言配合 switch 语句来实现。

var u USB = Phone{Name: "iPhone"}

switch v := u.(type) {

    case Phone:

        fmt.Printf("This is a phone named %s\n", v.Name)

    case Computer:

        fmt.Printf("This is a computer named %s\n", v.Brand)

    default:

        fmt.Println("Unknown USB device")

}

  • 当你需要将接口类型转换为具体类型以进行进一步处理时,可以使用类型断言来实现.

type Reader interface {

    Read(p []byte) (n int, err error)

}

type File struct {

    Name string

}

func (f File) Read(p []byte) (n int, err error) {

    // 实现 Reader 接口的 Read 方法

    return 0, nil

}

var r Reader = File{Name: "data.txt"}

// 需要将 Reader 接口类型转换为 File 类型以访问 File 的 Name 属性

file := r.(File) // 断言 r 是 File 类型

fmt.Printf("Reading from file: %s\n", file.Name) // 输出: Reading from file: data.txt