接口是 Go 语言实现多态的核心机制。本章将详细介绍接口的定义和使用。

什么是接口?

接口是一组方法的集合,它定义了对象的行为。任何实现了这些方法的类型,都自动实现了该接口。

接口示意:

┌─────────────────────────────────────┐
│          Shape 接口                  │
├─────────────────────────────────────┤
│  Area() float64                     │
│  Perimeter() float64                │
└─────────────────────────────────────┘
         ↑           ↑
         │           │
    ┌────┴────┐ ┌────┴────┐
    │ Rectangle│ │  Circle │
    └─────────┘ └─────────┘

接口定义

基本语法

type 接口名 interface {
    方法名1(参数) 返回值
    方法名2(参数) 返回值
}

示例

package main

import (
    "fmt"
    "math"
)

type Shape interface {
    Area() float64
    Perimeter() float64
}

type Rectangle struct {
    Width  float64
    Height float64
}

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

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

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
}

func main() {
    var s Shape

    s = Rectangle{Width: 10, Height: 5}
    fmt.Printf("矩形面积: %.2f\n", s.Area())

    s = Circle{Radius: 5}
    fmt.Printf("圆形面积: %.2f\n", s.Area())
}

接口实现

Go 的接口是隐式实现的,不需要显式声明 implements

package main

import "fmt"

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

type FileWriter struct{}

func (fw FileWriter) Write(data []byte) (int, error) {
    fmt.Printf("写入文件: %s\n", string(data))
    return len(data), nil
}

func main() {
    var w Writer = FileWriter{}
    w.Write([]byte("Hello"))
}

接口多态

package main

import "fmt"

type Animal interface {
    Speak() string
}

type Dog struct{}

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

type Cat struct{}

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

func MakeSound(a Animal) {
    fmt.Println(a.Speak())
}

func main() {
    dog := Dog{}
    cat := Cat{}

    MakeSound(dog)
    MakeSound(cat)
}

输出:

汪汪汪
喵喵喵

空接口

空接口 interface{} 没有任何方法,所有类型都实现了空接口:

package main

import "fmt"

func PrintAny(v interface{}) {
    fmt.Printf("值: %v, 类型: %T\n", v, v)
}

func main() {
    PrintAny(42)
    PrintAny("hello")
    PrintAny(3.14)
    PrintAny([]int{1, 2, 3})
}

空接口作为 Map 值

package main

import "fmt"

func main() {
    data := map[string]interface{}{
        "name":   "张三",
        "age":    25,
        "active": true,
        "scores": []int{90, 85, 88},
    }

    fmt.Printf("data: %v\n", data)
}

类型断言

基本用法

package main

import "fmt"

func main() {
    var i interface{} = "hello"

    s := i.(string)
    fmt.Println(s)

    n := i.(int)
    fmt.Println(n)
}

安全断言

package main

import "fmt"

func main() {
    var i interface{} = "hello"

    s, ok := i.(string)
    if ok {
        fmt.Printf("字符串: %s\n", s)
    }

    n, ok := i.(int)
    if ok {
        fmt.Printf("整数: %d\n", n)
    } else {
        fmt.Println("不是整数")
    }
}

类型切换

package main

import "fmt"

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

func main() {
    TypeCheck(42)
    TypeCheck("hello")
    TypeCheck(true)
    TypeCheck(3.14)
}

接口组合

package main

import "fmt"

type Reader interface {
    Read() string
}

type Writer interface {
    Write(string)
}

type ReadWriter interface {
    Reader
    Writer
}

type File struct {
    content string
}

func (f *File) Read() string {
    return f.content
}

func (f *File) Write(s string) {
    f.content = s
}

func main() {
    var rw ReadWriter = &File{}
    rw.Write("Hello, Go!")
    fmt.Println(rw.Read())
}

接口嵌套

package main

import "fmt"

type Speaker interface {
    Speak()
}

type Mover interface {
    Move()
}

type Animal interface {
    Speaker
    Mover
}

type Dog struct{}

func (d Dog) Speak() {
    fmt.Println("汪汪汪")
}

func (d Dog) Move() {
    fmt.Println("跑起来")
}

func main() {
    var a Animal = Dog{}
    a.Speak()
    a.Move()
}

接口值

接口值由两部分组成:类型和值:

package main

import "fmt"

func main() {
    var i interface{}

    fmt.Printf("类型: %T, 值: %v, 是否为nil: %v\n", i, i, i == nil)

    i = 42
    fmt.Printf("类型: %T, 值: %v\n", i, i)

    i = "hello"
    fmt.Printf("类型: %T, 值: %v\n", i, i)
}

实际案例

案例:日志系统

package main

import "fmt"

type Logger interface {
    Log(message string)
}

type ConsoleLogger struct{}

func (cl ConsoleLogger) Log(message string) {
    fmt.Printf("[Console] %s\n", message)
}

type FileLogger struct {
    Filename string
}

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

func Process(logger Logger, data string) {
    logger.Log("开始处理: " + data)
    logger.Log("处理完成")
}

func main() {
    consoleLogger := ConsoleLogger{}
    Process(consoleLogger, "数据1")

    fileLogger := FileLogger{Filename: "app.log"}
    Process(fileLogger, "数据2")
}

案例:支付系统

package main

import "fmt"

type PaymentMethod interface {
    Pay(amount float64) string
}

type CreditCard struct {
    Number string
}

func (cc CreditCard) Pay(amount float64) string {
    return fmt.Sprintf("信用卡 %s 支付 %.2f 元", cc.Number, amount)
}

type Alipay struct {
    Account string
}

func (a Alipay) Pay(amount float64) string {
    return fmt.Sprintf("支付宝 %s 支付 %.2f 元", a.Account, amount)
}

type WechatPay struct {
    OpenID string
}

func (w WechatPay) Pay(amount float64) string {
    return fmt.Sprintf("微信 %s 支付 %.2f 元", w.OpenID, amount)
}

func Checkout(method PaymentMethod, amount float64) {
    fmt.Println(method.Pay(amount))
}

func main() {
    Checkout(CreditCard{Number: "****1234"}, 100.00)
    Checkout(Alipay{Account: "user@example.com"}, 200.00)
    Checkout(WechatPay{OpenID: "wx123"}, 300.00)
}

总结

本章学习了 Go 语言的接口:

知识点说明
定义type Name interface { ... }
实现隐式实现,无需声明
多态同一接口不同实现
空接口interface{},可存储任意类型
类型断言v.(Type) 获取接口值的具体类型
接口组合嵌套多个接口