Go语言快速入门Go语言快速入门
首页
基础篇
进阶篇
高阶篇
实战篇
Go官方网站
编程指南
首页
基础篇
进阶篇
高阶篇
实战篇
Go官方网站
编程指南
  • 进阶篇

    • 🚀 进阶篇
    • 方法
    • 接口
    • 错误处理
    • Goroutine
    • Channel
    • 包管理
    • 单元测试

接口

接口是Go的核心特性,实现了多态和解耦。和Java最大的区别:隐式实现!这个设计简直太棒了,我第一次接触的时候就被惊艳到了。不需要显式声明implements,只要实现了方法就自动满足接口,这种鸭子类型的设计让代码更加灵活!

什么是接口?

接口定义了一组方法签名。任何类型只要实现了这些方法,就自动实现了该接口。

// 定义接口
type Speaker interface {
    Speak() string
}

// Dog实现了Speaker接口(隐式!无需声明)
type Dog struct {
    Name string
}

func (d Dog) Speak() string {
    return "汪汪!"
}

// Cat也实现了Speaker接口
type Cat struct {
    Name string
}

func (c Cat) Speak() string {
    return "喵喵!"
}

Java vs Go

// Java - 显式实现
public class Dog implements Speaker {
    @Override
    public String speak() { return "汪汪!"; }
}
// Go - 隐式实现(只要有Speak方法就自动实现)
type Dog struct{ Name string }
func (d Dog) Speak() string { return "汪汪!" }
// 无需写 implements!

接口的使用

// 接口类型的变量可以保存任何实现了该接口的值
var s Speaker

s = Dog{Name: "旺财"}
fmt.Println(s.Speak())  // 汪汪!

s = Cat{Name: "咪咪"}
fmt.Println(s.Speak())  // 喵喵!

多态函数

// 接受任何Speaker
func MakeSound(s Speaker) {
    fmt.Println(s.Speak())
}

func main() {
    dog := Dog{Name: "旺财"}
    cat := Cat{Name: "咪咪"}
    
    MakeSound(dog)  // 汪汪!
    MakeSound(cat)  // 喵喵!
    
    // Speaker切片
    animals := []Speaker{dog, cat}
    for _, animal := range animals {
        MakeSound(animal)
    }
}

接口的定义规范

// 接口命名通常以 -er 结尾
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

type Stringer interface {
    String() string
}

// 多个方法的接口
type ReadWriter interface {
    Read(p []byte) (n int, err error)
    Write(p []byte) (n int, err error)
}

接口组合

接口可以嵌入其他接口:

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

// 组合接口
type ReadWriter interface {
    Reader  // 嵌入Reader
    Writer  // 嵌入Writer
}

// 等价于
type ReadWriter interface {
    Read(p []byte) (n int, err error)
    Write(p []byte) (n int, err error)
}

空接口 interface{}

空接口没有任何方法,任何类型都实现了空接口。

// 空接口可以接收任何值
var anything interface{}

anything = 42
anything = "hello"
anything = []int{1, 2, 3}
anything = struct{ Name string }{"张三"}

// 函数接收任意类型
func PrintAnything(v interface{}) {
    fmt.Println(v)
}

PrintAnything(42)
PrintAnything("hello")
PrintAnything([]int{1, 2, 3})

Go 1.18+ 推荐使用 any

any 是 interface{} 的别名,更简洁:

var anything any = 42

类型断言

从接口类型获取具体类型:

var s Speaker = Dog{Name: "旺财"}

// 类型断言
dog := s.(Dog)
fmt.Println(dog.Name)  // 旺财

// 安全的类型断言(推荐!)
dog, ok := s.(Dog)
if ok {
    fmt.Println("是狗:", dog.Name)
} else {
    fmt.Println("不是狗")
}

// 如果断言失败会panic
// cat := s.(Cat)  // panic!

类型选择(Type Switch)

处理多种类型:

func describe(v interface{}) {
    switch val := v.(type) {
    case int:
        fmt.Printf("整数: %d\n", val)
    case string:
        fmt.Printf("字符串: %s\n", val)
    case bool:
        fmt.Printf("布尔值: %t\n", val)
    case []int:
        fmt.Printf("整数切片: %v\n", val)
    default:
        fmt.Printf("未知类型: %T\n", val)
    }
}

func main() {
    describe(42)           // 整数: 42
    describe("hello")      // 字符串: hello
    describe(true)         // 布尔值: true
    describe([]int{1,2})   // 整数切片: [1 2]
    describe(3.14)         // 未知类型: float64
}

接口的零值

接口的零值是 nil,调用nil接口的方法会panic。

var s Speaker  // nil
// s.Speak()   // panic: nil pointer dereference

if s != nil {
    s.Speak()
}

nil接口 vs 接口包含nil值

这是Go接口的一个坑:

type Speaker interface {
    Speak() string
}

type Dog struct{}
func (d *Dog) Speak() string { return "汪!" }

func main() {
    var s Speaker  // nil接口
    fmt.Println(s == nil)  // true
    
    var d *Dog     // nil指针
    s = d          // 接口不是nil了,但包含nil值
    fmt.Println(s == nil)  // false(注意!)
    
    // 调用方法会根据实现而定
    // s.Speak()  // 如果Speak方法不处理nil,可能panic
}

常用标准库接口

Stringer

// fmt包的Stringer接口
type Stringer interface {
    String() string
}

type Person struct {
    Name string
    Age  int
}

// 实现Stringer接口
func (p Person) String() string {
    return fmt.Sprintf("%s (%d岁)", p.Name, p.Age)
}

func main() {
    p := Person{Name: "张三", Age: 25}
    fmt.Println(p)  // 张三 (25岁)
}

error

// 内置的error接口
type error interface {
    Error() string
}

// 自定义错误
type MyError struct {
    Code    int
    Message string
}

func (e MyError) Error() string {
    return fmt.Sprintf("[%d] %s", e.Code, e.Message)
}

func doSomething() error {
    return MyError{Code: 404, Message: "未找到"}
}

io.Reader 和 io.Writer

import "io"

// 任何实现Read方法的类型都可以作为Reader
type Reader interface {
    Read(p []byte) (n int, err error)
}

// 任何实现Write方法的类型都可以作为Writer
type Writer interface {
    Write(p []byte) (n int, err error)
}

// 示例:复制数据
func Copy(dst io.Writer, src io.Reader) (int64, error) {
    // 从src读取,写入dst
}

// 可以用于文件、网络、缓冲区等

sort.Interface

import "sort"

// 实现这个接口就可以排序
type Interface interface {
    Len() int
    Less(i, j int) bool
    Swap(i, j int)
}

// 示例
type Person struct {
    Name string
    Age  int
}

type ByAge []Person

func (a ByAge) Len() int           { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }

func main() {
    people := []Person{
        {"张三", 25},
        {"李四", 30},
        {"王五", 20},
    }
    
    sort.Sort(ByAge(people))
    // [{王五 20} {张三 25} {李四 30}]
}

实战案例:支付系统

package main

import (
    "errors"
    "fmt"
    "time"
)

// 支付接口
type PaymentMethod interface {
    Pay(amount float64) error
    GetName() string
}

// 支付记录
type PaymentRecord struct {
    Method    string
    Amount    float64
    Status    string
    Timestamp time.Time
}

// ========== 微信支付 ==========
type WeChatPay struct {
    UserID   string
    Balance  float64
}

func (w *WeChatPay) Pay(amount float64) error {
    if amount > w.Balance {
        return errors.New("微信余额不足")
    }
    w.Balance -= amount
    fmt.Printf("💚 微信支付成功: ¥%.2f\n", amount)
    return nil
}

func (w *WeChatPay) GetName() string {
    return "微信支付"
}

// ========== 支付宝 ==========
type AliPay struct {
    UserID  string
    Balance float64
}

func (a *AliPay) Pay(amount float64) error {
    if amount > a.Balance {
        return errors.New("支付宝余额不足")
    }
    a.Balance -= amount
    fmt.Printf("💙 支付宝支付成功: ¥%.2f\n", amount)
    return nil
}

func (a *AliPay) GetName() string {
    return "支付宝"
}

// ========== 银行卡 ==========
type BankCard struct {
    CardNo   string
    BankName string
    Balance  float64
}

func (b *BankCard) Pay(amount float64) error {
    if amount > b.Balance {
        return errors.New("银行卡余额不足")
    }
    b.Balance -= amount
    fmt.Printf("💳 %s银行卡支付成功: ¥%.2f\n", b.BankName, amount)
    return nil
}

func (b *BankCard) GetName() string {
    return b.BankName + "银行卡"
}

// ========== 支付处理器 ==========
type PaymentProcessor struct {
    records []PaymentRecord
}

// 处理支付
func (p *PaymentProcessor) Process(method PaymentMethod, amount float64) error {
    record := PaymentRecord{
        Method:    method.GetName(),
        Amount:    amount,
        Timestamp: time.Now(),
    }
    
    if err := method.Pay(amount); err != nil {
        record.Status = "失败: " + err.Error()
        p.records = append(p.records, record)
        return err
    }
    
    record.Status = "成功"
    p.records = append(p.records, record)
    return nil
}

// 打印支付记录
func (p *PaymentProcessor) PrintRecords() {
    fmt.Println("\n📋 支付记录")
    fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
    for i, r := range p.records {
        fmt.Printf("%d. [%s] %s ¥%.2f - %s\n",
            i+1,
            r.Timestamp.Format("15:04:05"),
            r.Method,
            r.Amount,
            r.Status,
        )
    }
}

func main() {
    // 创建支付方式
    wechat := &WeChatPay{UserID: "wx123", Balance: 1000}
    alipay := &AliPay{UserID: "ali456", Balance: 500}
    bank := &BankCard{CardNo: "6222xxxx", BankName: "工商", Balance: 10000}
    
    // 创建支付处理器
    processor := &PaymentProcessor{}
    
    // 处理支付
    processor.Process(wechat, 88.8)
    processor.Process(alipay, 299)
    processor.Process(bank, 1999)
    processor.Process(alipay, 500)  // 余额不足
    
    // 打印记录
    processor.PrintRecords()
    
    // 打印余额
    fmt.Println("\n💰 账户余额")
    fmt.Printf("   微信: ¥%.2f\n", wechat.Balance)
    fmt.Printf("   支付宝: ¥%.2f\n", alipay.Balance)
    fmt.Printf("   银行卡: ¥%.2f\n", bank.Balance)
}

输出:

💚 微信支付成功: ¥88.80
💙 支付宝支付成功: ¥299.00
💳 工商银行卡支付成功: ¥1999.00

📋 支付记录
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. [10:30:00] 微信支付 ¥88.80 - 成功
2. [10:30:00] 支付宝 ¥299.00 - 成功
3. [10:30:00] 工商银行卡 ¥1999.00 - 成功
4. [10:30:00] 支付宝 ¥500.00 - 失败: 支付宝余额不足

💰 账户余额
   微信: ¥911.20
   支付宝: ¥201.00
   银行卡: ¥8001.00

实战案例:数据库抽象层

package main

import (
    "fmt"
    "time"
)

// 数据库接口
type Database interface {
    Connect() error
    Close() error
    Query(sql string) ([]map[string]interface{}, error)
    Execute(sql string) (int64, error)
}

// ========== MySQL实现 ==========
type MySQL struct {
    Host     string
    Port     int
    User     string
    Password string
    DBName   string
    connected bool
}

func (m *MySQL) Connect() error {
    fmt.Printf("🔌 连接MySQL: %s:%d/%s\n", m.Host, m.Port, m.DBName)
    m.connected = true
    return nil
}

func (m *MySQL) Close() error {
    fmt.Println("👋 关闭MySQL连接")
    m.connected = false
    return nil
}

func (m *MySQL) Query(sql string) ([]map[string]interface{}, error) {
    fmt.Printf("📖 MySQL查询: %s\n", sql)
    // 模拟返回数据
    return []map[string]interface{}{
        {"id": 1, "name": "张三"},
        {"id": 2, "name": "李四"},
    }, nil
}

func (m *MySQL) Execute(sql string) (int64, error) {
    fmt.Printf("✏️ MySQL执行: %s\n", sql)
    return 1, nil
}

// ========== PostgreSQL实现 ==========
type PostgreSQL struct {
    Host      string
    Port      int
    User      string
    Password  string
    DBName    string
    connected bool
}

func (p *PostgreSQL) Connect() error {
    fmt.Printf("🔌 连接PostgreSQL: %s:%d/%s\n", p.Host, p.Port, p.DBName)
    p.connected = true
    return nil
}

func (p *PostgreSQL) Close() error {
    fmt.Println("👋 关闭PostgreSQL连接")
    p.connected = false
    return nil
}

func (p *PostgreSQL) Query(sql string) ([]map[string]interface{}, error) {
    fmt.Printf("📖 PostgreSQL查询: %s\n", sql)
    return []map[string]interface{}{
        {"id": 1, "name": "王五"},
    }, nil
}

func (p *PostgreSQL) Execute(sql string) (int64, error) {
    fmt.Printf("✏️ PostgreSQL执行: %s\n", sql)
    return 1, nil
}

// ========== 数据库服务 ==========
type DBService struct {
    db Database
}

func NewDBService(db Database) *DBService {
    return &DBService{db: db}
}

func (s *DBService) Init() error {
    return s.db.Connect()
}

func (s *DBService) Shutdown() error {
    return s.db.Close()
}

func (s *DBService) GetUsers() ([]map[string]interface{}, error) {
    return s.db.Query("SELECT * FROM users")
}

func (s *DBService) CreateUser(name string) error {
    sql := fmt.Sprintf("INSERT INTO users (name, created_at) VALUES ('%s', '%s')",
        name, time.Now().Format("2006-01-02"))
    _, err := s.db.Execute(sql)
    return err
}

func main() {
    // 使用MySQL
    fmt.Println("===== 使用 MySQL =====")
    mysqlDB := &MySQL{
        Host:   "localhost",
        Port:   3306,
        DBName: "myapp",
    }
    
    service := NewDBService(mysqlDB)
    service.Init()
    service.GetUsers()
    service.CreateUser("赵六")
    service.Shutdown()
    
    fmt.Println()
    
    // 切换到PostgreSQL - 业务代码无需修改!
    fmt.Println("===== 使用 PostgreSQL =====")
    pgDB := &PostgreSQL{
        Host:   "localhost",
        Port:   5432,
        DBName: "myapp",
    }
    
    service = NewDBService(pgDB)
    service.Init()
    service.GetUsers()
    service.CreateUser("钱七")
    service.Shutdown()
}

接口设计原则

1. 接口要小

// ❌ 太大的接口
type Repository interface {
    Find(id int) (*User, error)
    FindAll() ([]*User, error)
    Create(user *User) error
    Update(user *User) error
    Delete(id int) error
    Search(query string) ([]*User, error)
    // ... 更多方法
}

// ✅ 小接口,按需组合
type Reader interface {
    Find(id int) (*User, error)
}

type Writer interface {
    Create(user *User) error
    Update(user *User) error
}

type Deleter interface {
    Delete(id int) error
}

type ReadWriter interface {
    Reader
    Writer
}

2. 接口由使用者定义

// 在使用的包中定义接口,而不是实现的包中
// 这样可以避免不必要的依赖

// user_service.go
type UserRepository interface {
    GetByID(id int) (*User, error)
}

type UserService struct {
    repo UserRepository
}

3. 返回具体类型,接收接口类型

// ✅ 好的设计
func NewMySQL() *MySQL { ... }  // 返回具体类型

func DoSomething(db Database) { ... }  // 接收接口

// ❌ 不推荐
func NewDatabase() Database { ... }  // 返回接口,隐藏了实现

练习

  1. 定义一个 Shape 接口,包含 Area() 和 Perimeter() 方法,实现 Circle 和 Rectangle
  2. 定义一个 Logger 接口,实现控制台日志和文件日志
  3. 实现一个简单的插件系统,定义 Plugin 接口
参考答案
package main

import (
    "fmt"
    "math"
    "time"
)

// 1. Shape接口
type Shape interface {
    Area() float64
    Perimeter() float64
}

type Circle struct {
    Radius float64
}

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

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

type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

// 2. Logger接口
type Logger interface {
    Log(message string)
    Error(message string)
}

type ConsoleLogger struct{}

func (l ConsoleLogger) Log(message string) {
    fmt.Printf("[LOG] %s: %s\n", time.Now().Format("15:04:05"), message)
}

func (l ConsoleLogger) Error(message string) {
    fmt.Printf("[ERR] %s: %s\n", time.Now().Format("15:04:05"), message)
}

type FileLogger struct {
    Filename string
}

func (l FileLogger) Log(message string) {
    fmt.Printf("[FILE:%s] LOG: %s\n", l.Filename, message)
}

func (l FileLogger) Error(message string) {
    fmt.Printf("[FILE:%s] ERR: %s\n", l.Filename, message)
}

// 3. Plugin接口
type Plugin interface {
    Name() string
    Init() error
    Execute() error
    Cleanup()
}

type HelloPlugin struct{}

func (p HelloPlugin) Name() string { return "HelloPlugin" }
func (p HelloPlugin) Init() error {
    fmt.Println("HelloPlugin初始化")
    return nil
}
func (p HelloPlugin) Execute() error {
    fmt.Println("Hello from plugin!")
    return nil
}
func (p HelloPlugin) Cleanup() {
    fmt.Println("HelloPlugin清理")
}

func main() {
    // 1. 测试Shape
    shapes := []Shape{
        Circle{Radius: 5},
        Rectangle{Width: 10, Height: 5},
    }
    for _, s := range shapes {
        fmt.Printf("面积: %.2f, 周长: %.2f\n", s.Area(), s.Perimeter())
    }
    
    // 2. 测试Logger
    var logger Logger = ConsoleLogger{}
    logger.Log("这是一条日志")
    
    logger = FileLogger{Filename: "app.log"}
    logger.Error("这是一条错误")
    
    // 3. 测试Plugin
    plugin := HelloPlugin{}
    plugin.Init()
    plugin.Execute()
    plugin.Cleanup()
}

接口是Go解耦的关键,下一节学习错误处理!

最近更新: 2025/12/27 13:26
Contributors: 王长安
Prev
方法
Next
错误处理