一、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