Golang中的接口

目录

一、接口的介绍

二、Golang接口的定义

三、空接口

四、类型断言

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

六、一个结构体实现多个接口

七、接口嵌套

一、接口的介绍

1. 现实生活中的接口

现实生活中,手机、相机、U盘都可以和电脑的USB接口建立连接。我们不需要关注USB卡槽大小是否一样,因为所有的USB接口都是按照统一的标准来设计的。

2. Golang中的接口(interface)

Golang中的接口是一种抽象数据类型 ,它定义了对象的行为规范,只定义规范,不实现。接口中定义的规范由具体的对象来实现。

通俗地说,接口就是一个标准,它约定实现接口的对象必须遵循接口的规范。

二、Golang接口的定义

在Golang中,接口(interface)是一种类型,一种抽象的类型 。接口是一组方法(method)的集合,接口中不能包含任何变量

  • 接口中的所有方法都没有方法体
  • 接口定义了对象的行为规范,只定义规范不实现
  • 接口体现了多态和高内聚低耦合的思想
  • 接口是一种数据类型,不需要显式实现。只要一个变量含有接口类型中的所有方法,该变量就实现了这个接口

接口的定义格式

go 复制代码
type 接口名 interface {
    方法名1(参数列表1) 返回值列表1
    方法名2(参数列表2) 返回值列表2
    ...
}

命名规范

  • 接口名通常使用 type 定义为自定义类型名,一般会在单词后加 er,如 WriterStringer
  • 方法名首字母大写时,可被包外的代码访问
  • 参数列表和返回值列表中的参数变量名可以省略

示例1:定义接口并由结构体实现

go 复制代码
package main

import "fmt"

// 定义 Usber 接口
type Usber interface {
    Start()
    Stop()
}

// Phone 结构体实现 Usber 接口
type Phone struct {
    Name string
}

func (p Phone) Start() {
    fmt.Println(p.Name, "开始工作")
}

func (p Phone) Stop() {
    fmt.Println(p.Name, "停止工作")
}

// Camera 结构体实现 Usber 接口
type Camera struct {
    Name string
}

func (c Camera) Start() {
    fmt.Println(c.Name, "开始工作")
}

func (c Camera) Stop() {
    fmt.Println(c.Name, "停止工作")
}

func main() {
    // Phone 实现接口
    phone := Phone{Name: "小米手机"}
    var p Usber = phone
    p.Start()
    p.Stop()

    // Camera 实现接口
    camera := Camera{Name: "佳能相机"}
    var c Usber = camera
    c.Start()
    c.Stop()
}

示例2:接口作为函数参数

go 复制代码
package main

import "fmt"

type Usber interface {
    Start()
    Stop()
}

type Phone struct {
    Name string
}

func (p Phone) Start() {
    fmt.Println(p.Name, "开始工作")
}

func (p Phone) Stop() {
    fmt.Println(p.Name, "停止工作")
}

type Camera struct {
    Name string
}

func (c Camera) Start() {
    fmt.Println(c.Name, "开始工作")
}

func (c Camera) Stop() {
    fmt.Println(c.Name, "停止工作")
}

// Computer 结构体的 Work 方法接收 Usber 接口类型
type Computer struct{}

func (c Computer) Work(usb Usber) {
    usb.Start()
    usb.Stop()
}

func main() {
    computer := Computer{}

    phone := Phone{Name: "小米手机"}
    camera := Camera{Name: "佳能相机"}

    computer.Work(phone)
    computer.Work(camera)
}

三、空接口

Golang中的接口可以不定义任何方法,没有定义任何方法的接口就是空接口

  • 空接口表示没有任何约束
  • 任何类型变量都可以实现空接口
  • 空接口在实际项目中应用广泛,可表示任意数据类型

示例1:空接口接收任意类型

go 复制代码
func main() {
    var x interface{} // 定义空接口

    s := "你好golang"
    x = s
    fmt.Printf("type:%T value:%v\n", x, x)

    i := 100
    x = i
    fmt.Printf("type:%T value:%v\n", x, x)

    b := true
    x = b
    fmt.Printf("type:%T value:%v\n", x, x)
}

示例2:空接口作为函数参数

go 复制代码
func show(a interface{}) {
    fmt.Printf("type:%T value:%v\n", a, a)
}

示例3:map 的值使用空接口

go 复制代码
var studentInfo = make(map[string]interface{})
studentInfo["name"] = "张三"
studentInfo["age"] = 18
studentInfo["married"] = false
fmt.Println(studentInfo)

示例4:切片使用空接口

go 复制代码
var slice = []interface{}{"张三", 20, true, 32.2}
fmt.Println(slice)

四、类型断言

接口值由具体类型具体类型的值 两部分组成,分别称为接口的动态类型动态值

类型断言用于判断空接口中值的类型。

语法格式

go 复制代码
x.(T)
  • x:类型为 interface{} 的变量
  • T:断言 x 可能的类型

返回值

  • 第一个值:x 转换为 T 类型后的变量
  • 第二个值:布尔值,true 表示断言成功,false 表示断言失败

示例1:单次类型断言

go 复制代码
func main() {
    var x interface{}
    x = "Hello golang"

    v, ok := x.(string)
    if ok {
        fmt.Println(v)
    } else {
        fmt.Println("类型断言失败")
    }
}

示例2:使用 switch 进行类型断言

注意:类型.(type) 只能结合 switch 语句使用。

go 复制代码
func justifyType(x interface{}) {
    switch v := x.(type) {
    case string:
        fmt.Printf("x is a string, value is %v\n", v)
    case int:
        fmt.Printf("x is an int, value is %v\n", v)
    case bool:
        fmt.Printf("x is a bool, value is %v\n", v)
    default:
        fmt.Println("unsupported type!")
    }
}

⚠️ 建议:只有当两个或两个以上的具体类型必须以相同方式处理时,才需要定义接口。不要为了接口而写接口,那样只会增加不必要的抽象和运行时损耗。

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

值接收者

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

go 复制代码
package main

import "fmt"

type Usb interface {
    Start()
    Stop()
}

type Phone struct {
    Name string
}

// 值接收者
func (p Phone) Start() {
    fmt.Println(p.Name, "开始工作")
}

func (p Phone) Stop() {
    fmt.Println("phone 停止")
}

func main() {
    // 值类型可以赋值给接口
    phone1 := Phone{Name: "小米手机"}
    var p1 Usb = phone1
    p1.Start()

    // 指针类型也可以赋值给接口
    phone2 := &Phone{Name: "苹果手机"}
    var p2 Usb = phone2
    p2.Start()
}

指针接收者

如果结构体中的方法是指针接收者 ,那么只有结构体指针类型 可以赋值给接口变量,结构体值类型不能赋值给接口变量。

go 复制代码
package main

import "fmt"

type Usb interface {
    Start()
    Stop()
}

type Phone struct {
    Name string
}

// 指针接收者
func (p *Phone) Start() {
    fmt.Println(p.Name, "开始工作")
}

func (p *Phone) Stop() {
    fmt.Println("phone 停止")
}

func main() {
    // 错误写法:值类型不能赋值给接口
    /*
    phone1 := Phone{Name: "小米手机"}
    var p1 Usb = phone1  // 编译错误
    p1.Start()
    */

    // 正确写法:指针类型可以赋值给接口
    phone2 := &Phone{Name: "苹果手机"}
    var p2 Usb = phone2
    p2.Start()
}

六、一个结构体实现多个接口

Golang中一个结构体可以实现多个接口。

go 复制代码
package main

import "fmt"

type AInterface interface {
    GetInfo() string
}

type BInterface interface {
    SetInfo(name string, age int)
}

type People struct {
    Name string
    Age  int
}

func (p People) GetInfo() string {
    return fmt.Sprintf("姓名:%v 年龄:%d", p.Name, p.Age)
}

func (p *People) SetInfo(name string, age int) {
    p.Name = name
    p.Age = age
}

func main() {
    people := &People{
        Name: "张三",
        Age:  20,
    }

    // people 同时实现了 AInterface 和 BInterface
    var p1 AInterface = people
    var p2 BInterface = people

    fmt.Println(p1.GetInfo())
    p2.SetInfo("李四", 30)
    fmt.Println(p1.GetInfo())
}

七、接口嵌套

接口与接口之间可以通过嵌套创建新的接口。

go 复制代码
package main

import "fmt"

type SayInterface interface {
    say()
}

type MoveInterface interface {
    move()
}

// 接口嵌套
type Animal interface {
    SayInterface
    MoveInterface
}

type Cat struct {
    name string
}

func (c Cat) say() {
    fmt.Println("喵喵喵")
}

func (c Cat) move() {
    fmt.Println("猫会动")
}

func main() {
    var x Animal
    x = Cat{name: "花花"}
    x.move()
    x.say()
}
相关推荐
赫媒派3 小时前
Gin 12年零破坏API,架构哲学如何练成?
后端·go·gin
唐青枫7 小时前
别再只会 if err != nil:Go error 从错误链到工程实战详解
go
小满zs21 小时前
Go语言第二章(小无相功)
后端·go
妙码生花21 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(十九):点选验证码代码逐行目检
前端·后端·go
老鹰86221 小时前
Google Wire 被官方抛弃,Uber Fx 启动就 panic,Go DI 还有救吗?
go
golang学习记1 天前
Go面试官:说说struct{}为什么占用0字节
go
喵个咪2 天前
Go Wind UBA 拆解系列 - 架构总览:三服务、数据流与契约优先
大数据·后端·go
喵个咪2 天前
Go Wind UBA 拆解系列 - 多租户与安全:两套隔离机制的边界
大数据·后端·go
喵个咪2 天前
Go Wind UBA 拆解系列 - OLAP 与 SQL 硬核:25 个分析模型怎么落地
大数据·后端·go