Go语言的接口

一、Go 接口定义

接口(interface)是 Go 语言中的一种类型,用于定义行为的集合,它通过描述类型必须实现的方法,规定了类型的行为契约。

Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。

Go 的接口设计简单却功能强大,是实现多态和解耦的重要工具。

接口可以让我们将不同的类型绑定到一组公共的方法上,从而实现多态和灵活的设计。

一言以蔽之:

Go 的接口 = 行为的集合,关注"你能做什么",而不是"你是谁"

最重要的特点:

  • 隐式实现(无需 implements

  • 面向行为,而不是类型层级

  • 接口通常很小(1~2 个方法)

  • 只要一个类型实现了接口要求的所有方法,该类型就自动被认为实现了该接口。

接口类型变量

  • 接口变量可以存储实现该接口的任意值。
  • 接口变量实际上包含了两个部分:
    • 动态类型:存储实际的值类型。
    • 动态值:存储具体的值。

零值接口

  • 接口的零值是 nil
  • 一个未初始化的接口变量其值为 nil,且不包含任何动态类型或值。

空接口

  • 定义为 interface{},可以表示任何类型。

接口的常见用法

  1. 多态:不同类型实现同一接口,实现多态行为。
  2. 解耦:通过接口定义依赖关系,降低模块之间的耦合。
  3. 泛化 :使用空接口 interface{} 表示任意类型。

二、接口的基本定义

Go 复制代码
type Reader interface {
    Read(p []byte) (n int, err error)
}

三、隐式实现

1.定义一个接口

Go 复制代码
type Flyable interface {
    Fly() string
}

2.普通 struct 实现方法

Go 复制代码
type Bird struct{}

func (b Bird) Fly() string {
    return "bird is flying"
}

3.直接赋值成功

Go 复制代码
func main() {
    var f Flyable
    f = Bird{}   // 自动匹配
    fmt.Println(f.Fly())
}

再比如

Go 复制代码
type Shape interface {
    Area() float64
    Perimeter() float64
}
  • Shape 是一个接口,定义了两个方法:AreaPerimeter
  • 任意类型只要实现了这两个方法,就被认为实现了 Shape 接口。

实现接口: 类型通过实现接口要求的所有方法来实现接口。

Go 复制代码
package main

import (
        "fmt"
        "math"
)

// 定义接口
type Shape interface {
        Area() float64
        Perimeter() float64
}

// 定义一个结构体
type Circle struct {
        Radius float64
}

// Circle 实现 Shape 接口
func (c Circle) Area() float64 {
        return math.Pi * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
        return 2 * math.Pi * c.Radius
}

func main() {
        c := Circle{Radius: 5}
        var s Shape = c // 接口变量可以存储实现了接口的类型
        fmt.Println("Area:", s.Area())
        fmt.Println("Perimeter:", s.Perimeter())
}

四、接口作为参数(依赖倒置)

这是 Go 最常见、也是最"优雅"的用法。

Go 复制代码
type Logger interface {
    Log(msg string)
}

func Process(l Logger) {
    l.Log("processing...")
}

实现 1:控制台日志

Go 复制代码
type ConsoleLogger struct{}

func (c ConsoleLogger) Log(msg string) {
    fmt.Println(msg)
}

实现 2:文件日志

Go 复制代码
type FileLogger struct{}

func (f FileLogger) Log(msg string) {
    fmt.Println("write to file:", msg)
}
Go 复制代码
Process(ConsoleLogger{})
Process(FileLogger{})

五、空接口 interface{}

Go 复制代码
var x interface{}
x = 10
x = "hello"
x = true

可接收任何类型

类型断言

Go 复制代码
v, ok := x.(string)
if ok {
    fmt.Println(v)
}

type switch(更优雅)

Go 复制代码
switch v := x.(type) {
case int:
    fmt.Println("int", v)
case string:
    fmt.Println("string", v)
default:
    fmt.Println("unknown")
}

六、接口组合

Go 复制代码
type Reader interface {
    Read()
}

type Writer interface {
    Write()
}

type ReadWriter interface {
    Reader
    Writer
}

七、动态值和动态类型

接口变量实际上包含了两部分:

  1. 动态类型:接口变量存储的具体类型。
  2. 动态值:具体类型的值。

动态值和动态类型示例:

Go 复制代码
package main

import "fmt"

func main() {
        var i interface{} = 42
        fmt.Printf("Dynamic type: %T, Dynamic value: %v\n", i, i)
}

八、接口的零值

接口的零值是 nil。

当接口变量的动态类型和动态值都为 nil 时,接口变量为 nil。

接口零值示例:

Go 复制代码
package main

import "fmt"

func main() {
        var i interface{}
        fmt.Println(i == nil) // 输出:true
}

九、接口值的本质

Go 复制代码
var r Reader

接口值内部其实是一个 二元组

bash 复制代码
(type, value)
情况 是否为 nil
var r Reader = nil
var b *Bird = nil; r = b 否(常见坑)

经典坑

Go 复制代码
var b *Bird = nil
var f Flyable = b

fmt.Println(f == nil) // false 

接口不为 nil,但内部值是 nil

十、Go 标准库里的"接口哲学"

Go 标准库接口都极小

Go 复制代码
type io.Reader interface {
    Read(p []byte) (n int, err error)
}

只要实现了 Read

  • 文件

  • 网络

  • 内存

  • 压缩流

全部通用

这就是 Go 接口设计的巅峰案例

相关推荐
清风徐来QCQ10 小时前
Cookie和JWT
后端·cookie
2301_7806698610 小时前
List(特有方法、遍历方式、ArrayList底层原理、LinkedList底层原理,二者区别)
java·数据结构·后端·list
浮尘笔记10 小时前
Go语言中的同步等待组和单例模式:sync.WaitGroup和sync.Once
开发语言·后端·单例模式·golang
lsx20240610 小时前
C++ 变量作用域
开发语言
小鸡脚来咯10 小时前
设计模式面试介绍指南
java·开发语言·单例模式
小北方城市网10 小时前
GEO 全场景智能生态:自适应架构重构与极限算力协同落地
开发语言·人工智能·python·重构·架构·量子计算
十五年专注C++开发10 小时前
CMake进阶:核心命令get_filename_component 完全详解
开发语言·c++·cmake·跨平台编译
Blossom.11810 小时前
工业级扩散模型优化实战:从Stable Diffusion到LCM的毫秒级生成
开发语言·人工智能·python·深度学习·机器学习·stable diffusion·transformer
嘿嘿潶黑黑10 小时前
关于QButtonGroup 在Qt5和Qt6之间的差异
开发语言·qt