<返回目录     Powered by claud/xia兄

第7课: 接口

接口基础

接口是Go语言中实现多态的核心机制。接口定义了一组方法签名,任何实现了这些方法的类型都自动实现了该接口。

接口定义

package main

import "fmt"

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

// Dog类型实现Speaker接口
type Dog struct {
    Name string
}

func (d Dog) Speak() string {
    return "Woof! I'm " + d.Name
}

// Cat类型实现Speaker接口
type Cat struct {
    Name string
}

func (c Cat) Speak() string {
    return "Meow! I'm " + c.Name
}

func main() {
    var s Speaker

    s = Dog{Name: "Buddy"}
    fmt.Println(s.Speak())  // Woof! I'm Buddy

    s = Cat{Name: "Whiskers"}
    fmt.Println(s.Speak())  // Meow! I'm Whiskers
}

接口的隐式实现

Go语言的接口实现是隐式的,不需要显式声明。只要类型实现了接口的所有方法,就自动实现了该接口。

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

type FileWriter struct {
    filename string
}

// FileWriter自动实现了Writer接口
func (fw FileWriter) Write(data []byte) (int, error) {
    fmt.Printf("写入文件 %s: %s\n", fw.filename, string(data))
    return len(data), nil
}

func main() {
    var w Writer = FileWriter{filename: "test.txt"}
    w.Write([]byte("Hello, World!"))
}

空接口

空接口interface{}不包含任何方法,因此所有类型都实现了空接口。空接口可以存储任意类型的值。

func main() {
    var i interface{}

    i = 42
    fmt.Println(i)  // 42

    i = "hello"
    fmt.Println(i)  // hello

    i = []int{1, 2, 3}
    fmt.Println(i)  // [1 2 3]

    // 空接口切片
    slice := []interface{}{1, "hello", 3.14, true}
    for _, v := range slice {
        fmt.Printf("%v (%T)\n", v, v)
    }
}

类型断言

类型断言用于从接口值中提取底层具体类型的值。

基本类型断言

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

    // 类型断言
    s := i.(string)
    fmt.Println(s)  // hello

    // 安全的类型断言
    s, ok := i.(string)
    if ok {
        fmt.Println("字符串:", s)
    }

    // 断言失败
    n, ok := i.(int)
    if !ok {
        fmt.Println("不是整数")
    }
}

类型switch

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

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

接口组合

接口可以通过嵌入其他接口来组合。

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

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

type Closer interface {
    Close() error
}

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

type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}

type File struct {
    name string
}

func (f *File) Read(p []byte) (int, error) {
    fmt.Println("读取文件:", f.name)
    return 0, nil
}

func (f *File) Write(p []byte) (int, error) {
    fmt.Println("写入文件:", f.name)
    return len(p), nil
}

func (f *File) Close() error {
    fmt.Println("关闭文件:", f.name)
    return nil
}

func main() {
    var rwc ReadWriteCloser = &File{name: "test.txt"}
    rwc.Read(nil)
    rwc.Write([]byte("data"))
    rwc.Close()
}

接口值

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

type Speaker interface {
    Speak() string
}

type Dog struct {
    Name string
}

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

func main() {
    var s Speaker
    fmt.Printf("(%v, %T)\n", s, s)  // (, )

    s = Dog{Name: "Buddy"}
    fmt.Printf("(%v, %T)\n", s, s)  // ({Buddy}, main.Dog)
}

接口的nil值

type Speaker interface {
    Speak() string
}

type Dog struct {
    Name string
}

func (d *Dog) Speak() string {
    if d == nil {
        return "nil dog"
    }
    return "Woof! I'm " + d.Name
}

func main() {
    var s Speaker
    fmt.Println(s == nil)  // true

    var d *Dog
    s = d
    fmt.Println(s == nil)  // false (接口不为nil,但底层值为nil)
    fmt.Println(s.Speak()) // nil dog
}

标准库中的接口

io.Reader和io.Writer

package main

import (
    "fmt"
    "io"
    "strings"
)

func main() {
    // strings.Reader实现了io.Reader接口
    r := strings.NewReader("Hello, Go!")

    buf := make([]byte, 8)
    for {
        n, err := r.Read(buf)
        fmt.Printf("读取 %d 字节: %s\n", n, buf[:n])
        if err == io.EOF {
            break
        }
    }
}

fmt.Stringer

type Person struct {
    Name string
    Age  int
}

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

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

error接口

type error interface {
    Error() string
}

// 自定义错误类型
type MyError struct {
    Code int
    Msg  string
}

func (e *MyError) Error() string {
    return fmt.Sprintf("错误 %d: %s", e.Code, e.Msg)
}

func doSomething() error {
    return &MyError{Code: 404, Msg: "未找到资源"}
}

func main() {
    if err := doSomething(); err != nil {
        fmt.Println(err)  // 错误 404: 未找到资源
    }
}

接口的最佳实践

接受接口,返回结构体

// 好的设计:接受接口
func ProcessData(r io.Reader) error {
    // 处理数据
    return nil
}

// 不好的设计:接受具体类型
func ProcessFile(f *os.File) error {
    // 处理文件
    return nil
}

小接口原则

// 好的设计:小而专注的接口
type Reader interface {
    Read(p []byte) (n int, err error)
}

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

// 不好的设计:大而全的接口
type FileOperations interface {
    Read(p []byte) (n int, err error)
    Write(p []byte) (n int, err error)
    Close() error
    Seek(offset int64, whence int) (int64, error)
    // ... 更多方法
}

综合示例

示例1:形状计算

package main

import (
    "fmt"
    "math"
)

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

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)
}

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 printShapeInfo(s Shape) {
    fmt.Printf("面积: %.2f, 周长: %.2f\n", s.Area(), s.Perimeter())
}

func main() {
    rect := Rectangle{Width: 10, Height: 5}
    circle := Circle{Radius: 5}

    printShapeInfo(rect)
    printShapeInfo(circle)
}

示例2:数据库接口

package main

import "fmt"

type Database interface {
    Connect() error
    Query(sql string) ([]map[string]interface{}, error)
    Close() error
}

type MySQL struct {
    host string
    port int
}

func (m *MySQL) Connect() error {
    fmt.Printf("连接到MySQL %s:%d\n", m.host, m.port)
    return nil
}

func (m *MySQL) Query(sql string) ([]map[string]interface{}, error) {
    fmt.Println("执行MySQL查询:", sql)
    return nil, nil
}

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

type PostgreSQL struct {
    host string
    port int
}

func (p *PostgreSQL) Connect() error {
    fmt.Printf("连接到PostgreSQL %s:%d\n", p.host, p.port)
    return nil
}

func (p *PostgreSQL) Query(sql string) ([]map[string]interface{}, error) {
    fmt.Println("执行PostgreSQL查询:", sql)
    return nil, nil
}

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

func executeQuery(db Database, sql string) {
    db.Connect()
    db.Query(sql)
    db.Close()
}

func main() {
    mysql := &MySQL{host: "localhost", port: 3306}
    postgres := &PostgreSQL{host: "localhost", port: 5432}

    executeQuery(mysql, "SELECT * FROM users")
    executeQuery(postgres, "SELECT * FROM users")
}

示例3:排序接口

package main

import (
    "fmt"
    "sort"
)

type Person struct {
    Name string
    Age  int
}

type ByAge []Person

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

func main() {
    people := []Person{
        {"Alice", 25},
        {"Bob", 20},
        {"Charlie", 30},
    }

    sort.Sort(ByAge(people))
    fmt.Println(people)
}
接口设计原则:

实践练习

  1. 定义一个Animal接口,实现Dog、Cat、Bird三种动物
  2. 实现一个支付接口,支持支付宝、微信、银行卡支付
  3. 创建一个日志接口,实现文件日志和控制台日志
  4. 实现sort.Interface接口,对自定义结构体排序
  5. 创建一个缓存接口,实现内存缓存和Redis缓存
  6. 实现io.Reader和io.Writer接口
  7. 创建一个通知接口,支持邮件、短信、推送通知
  8. 实现一个插件系统(使用接口)
常见陷阱: