美文网首页
Go 学习笔记 11 | Golang 接口详解

Go 学习笔记 11 | Golang 接口详解

作者: Wonz | 来源:发表于2020-12-15 19:53 被阅读0次

    一、Golang 接口

    Golang 中接口定义了对象的行为规范,只定义规范不实现。接口中定义的规范由具体的对象来实现。

    package main
    
    import (
        "fmt"
    )
    
    //接口是一个规范
    type Usber interface {  // 最好以 er 结尾表示接口
        start()
        stop()
    }
    
    // 如果接口里有方法的话,必须要通过结构体或者通过自定义类型实现这个接口。
    
    type Phone struct {
        Name string
    }
    
    // 手机要实现 usb 接口的话必须得实现 usb 接口中的所有方法
    
    func (p Phone) start() {
        fmt.Println(p.Name, "启动")
    }
    
    func (p Phone) stop() {
        fmt.Println(p.Name, "关机")
    }
    
    func main() {
        p := Phone{
            Name: "华为手机",
        }
        p.start()
    
        var p1 Usber  // Golang 中接口就是一个数据类型
        p1 = p  // 表示手机实现 Usb 接口
    
        p1.start()
        p1.stop()
    }
    

    输出:

    华为手机 启动
    华为手机 启动
    华为手机 关机
    

    空接口

    空接口表示没有任何约束,因此任何类型变量都可以实现空接口。

    package main
    
    import (
        "fmt"
    )
    
    // Golang 中空接口也可以直接当作类型来使用,可以表示任意类型
    func main() {
        var a interface{}  // 空接口可以接收任意类型
        a = 20
        fmt.Printf("值: %v 类型:%T\n", a, a)
        a = "你好golang"
        fmt.Printf("值: %v 类型:%T\n", a, a)
        a = true
        fmt.Printf("值: %v 类型:%T\n", a, a)   
    }
    

    输出:

    值: 20 类型:int
    值: 你好golang 类型:string
    值: true 类型:bool
    

    1. 空接口可以作为函数的参数

    package main
    
    import (
        "fmt"
    )
    
    // 1、空接口可以作为函数的参数
    func show(a interface{}) {
        fmt.Printf("值:%v 类型:%T\n", a, a)
    }
    
    // Golang 中空接口也可以直接当作类型来使用,可以表示任意类型
    func main() {   
        show(20)
        show("你好golang")
        slice := []int{1, 2, 3, 4}
        show(slice)
    }
    

    输出:

    值:20 类型:int
    值:你好golang 类型:string
    值:[1 2 3 4] 类型:[]int
    

    2. map 的值实现空接口

    package main
    
    import (
        "fmt"
    )
    
    // 2、map 的值实现空接口
    func show(a interface{}) {
        fmt.Printf("值:%v 类型:%T\n", a, a)
    }
    
    func main() {
        var m1 = make(map[string]interface{})
    
        m1["username"] = "张三"
        m1["age"] = 20
        m1["married"] = true
        
        fmt.Println(m1)
    
        var s1 = []interface{}{1, 2, "你好", true}
        fmt.Println(s1)
    }
    

    输出:

    map[age:20 married:true username:张三]
    [1 2 你好 true]
    

    类型断言

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        var a interface{}
        a = "你好golang"
        v, ok := a.(string)
        if ok {
            fmt.Println("a就是一个string类型,值是:", v)
        } else {
            fmt.Println("断言失败")
        }
    }
    

    输出:

    a就是一个string类型,值是: 你好golang
    

    另一种写法:

    package main
    
    import (
        "fmt"
    )
    
    // 1、X.(T) 括号里表示 X 可能是的类型
    func MyPrint1(x interface{}) {
        if _, ok := x.(string); ok {
            fmt.Println("string类型")
        } else if _, ok := x.(int); ok {
            fmt.Println("int类型")
        } else if _, ok := x.(bool); ok {
            fmt.Println("bool类型")
        }
    }
    
    // 2、类型.(type)只能结合 switch 语句使用
    func MyPrint2(x interface{}) {
        switch x.(type) {
        case int:
            fmt.Println("int类型")
        case string:
            fmt.Println("string类型")
        case bool:
            fmt.Println("bool类型")
        default:
            fmt.Println("传入错误...")
        }
    }
    
    func main() {
        MyPrint1("你好golang")
        MyPrint1(true)
        MyPrint1(20)
    
        
        MyPrint2(true)
        MyPrint2(20)
        MyPrint2("你好golang")
    }
    

    输出:

    string类型
    bool类型
    int类型
    bool类型
    int类型
    string类型
    

    二、结构体值接收者实现接口

    值接收者:如果结构体中的方法是值接收者,那么实例化后的结构体值类型和结构体指针类型都可以赋值给接口类型变量。

    package main
    
    import (
        "fmt"
    )
    
    // 接口是一个规范
    type Usber interface {  // 最好以 er 结尾表示接口
        start()
        stop()
    }
    
    // 如果接口里有方法的话,必须要通过结构体或者通过自定义类型实现这个接口。
    
    type Phone struct {
        Name string
    }
    
    // 手机要实现 usb 接口的话必须得实现 usb 接口中的所有方法
    
    func (p Phone) start() {
        fmt.Println(p.Name, "启动")
    }
    
    func (p Phone) stop() {
        fmt.Println(p.Name, "关机")
    }
    
    func main() {
        // 结构体值接收者实例化后的结构体值类型和结构体指针类型都可以赋值给接口变量
        var p1 = Phone{
            Name: "小米手机",
        }
    
        var p2 Usber = p1  // 表示让 Phone 实现 Usb 的接口
        p2.start()
    
        var p3 = &Phone{
            Name: "苹果手机",
        }
    
        var p4 Usber = p3
        p4.start()
    }
    

    输出:

    小米手机 启动
    苹果手机 启动
    

    指针类型

    package main
    
    import (
        "fmt"
    )
    
    // 接口是一个规范
    type Usber interface {  // 最好以 er 结尾表示接口
        start()
        stop()
    }
    
    // 如果接口里有方法的话,必须要通过结构体或者通过自定义类型实现这个接口。
    
    type Phone struct {
        Name string
    }
    
    // 手机要实现 usb 接口的话必须得实现 usb 接口中的所有方法
    
    func (p *Phone) start() {  // 指针接收者
        fmt.Println(p.Name, "启动")
    }
    
    func (p *Phone) stop() {
        fmt.Println(p.Name, "关机")
    }
    
    
    func main() {
        /*
            // 错误写法
            var phone1 = Phone{
                Name: "小米手机",
            }
    
            var p1 Usber = phone1  // 表示让 Phone 实现 Usb 的接口
            p1.start()
        */
    
        var phone1 = &Phone{
            Name: "小米",
        }
        var p1 Usber = phone1
        p1.start()
    }
    

    输出:

    小米 启动
    

    结构体值接收者和指针接收者实现接口的区别

    值接收者:如果结构体中的方法是值接收者,那么实例化后结构体值类型结构体指针类型都可以赋值给接口变量。

    指针接收者:如果结构体中的方法是指针接收者,那么实例化后结构体指针类型都可以赋值给接口变量,结构体值类型没法赋值给接口变量。

    package main
    
    import (
        "fmt"
    )
    
    type Animaler interface {
        SetName(string)
        GetName() string
    }
    
    type Dog struct {
        Name string
    }
    
    func (d *Dog) SetName(name string) {
        d.Name = name
    }
    
    func (d Dog) GetName() string {
        return d.Name
    }
    
    type Cat struct {
        Name string
    }
    
    func (c *Cat) SetName(name string) {
        c.Name = name
    }
    
    func (c Cat) GetName() string {
        return c.Name
    }
    
    func main() {
        // Dog 实现 Animal 的接口
        d := &Dog{
            Name: "小黑",
        }
        var d1 Animaler = d
        fmt.Println(d1.GetName())
        d1.SetName("小黄")
        fmt.Println(d1.GetName())
    
        // Cat 实现 Animal 的接口
        c := &Cat{
            Name: "小花",
        }
        var c1 Animaler = c
        fmt.Println(c1.GetName())
    }
    

    输出:

    小黑
    小黄
    小花
    

    接口嵌套

    package main
    
    import (
        "fmt"
    )
    
    type Ainterface interface {
        SetName(string)
    }
    
    type Binterface interface {
        GetName() string
    }
    
    type Animaler interface {  // 接口的嵌套
        Ainterface
        Binterface
    }
    
    type Dog struct {
        Name string
    }
    
    func (d *Dog) SetName(name string) {
        d.Name = name
    }
    
    func (d Dog) GetName() string {
        return d.Name
    }
    
    func main() {
        d := &Dog{
            Name: "小黑",
        }
        var d1 Animaler = d
        d1.SetName("小花")
        fmt.Println(d1.GetName())
    }
    

    输出:

    小花
    

    三、Golang 中空接口和类型断言使用细节

    package main
    
    import (
        "fmt"
    )
    
    type Address struct {
        Name string
        Phone int
    }
    
    // Golang中空接口和类型断言使用细节
    func main() {
        var userinfo = make(map[string]interface{})
        userinfo["username"] = "张三"
        userinfo["age"] = 20
        userinfo["hobby"] = []string{"睡觉", "吃饭"}
    
        fmt.Println(userinfo["age"])  // 20
        fmt.Println(userinfo["hobby"])  // [睡觉 吃饭]
    
        var address = Address {
            Name: "李四",
            Phone: 123456,
        }
        fmt.Println(address.Name)  // 李四
    
        userinfo["address"] = address
        fmt.Println(userinfo["address"])  // {李四 123456}
    
        hobby2, _ := userinfo["hobby"].([]string)  // 类型断言
    
        fmt.Println(hobby2[1])  // 吃饭
    
        address2, _ := userinfo["address"].(Address)  // 类型断言
        fmt.Println(address2.Name, address2.Phone)  // 李四 123456
    }
    

    输出:

    20
    [睡觉 吃饭]
    李四
    {李四 123456}
    吃饭
    李四 123456
    

    四、参考教程

    Golang 教程 P37-P40

    相关文章

      网友评论

          本文标题:Go 学习笔记 11 | Golang 接口详解

          本文链接:https://www.haomeiwen.com/subject/ilyuiktx.html