Go 接口

接口定义

基本语法

package main
 
import "fmt"
 
// 定义接口
type Writer interface {
    Write([]byte) (int, error)
}
 
// 接口可以包含多个方法
type Reader interface {
    Read([]byte) (int, error)
}
 
type ReadWriter interface {
    Reader  // 嵌入接口
    Writer  // 嵌入接口
}
 
// 实现接口(隐式实现)
type File struct {
    name string
}
 
// File 实现了 Writer 接口
func (f *File) Write(data []byte) (int, error) {
    fmt.Printf("写入文件 %s: %s\n", f.name, string(data))
    return len(data), nil
}
 
// File 实现了 Reader 接口
func (f *File) Read(data []byte) (int, error) {
    fmt.Printf("从文件 %s 读取\n", f.name)
    copy(data, []byte("文件内容"))
    return len("文件内容"), nil
}
 
func main() {
    // File 实现了 ReadWriter 接口
    var rw ReadWriter = &File{name: "test.txt"}
    
    data := []byte("Hello, World!")
    rw.Write(data)  // 写入文件 test.txt: Hello, World!
    
    buffer := make([]byte, 100)
    rw.Read(buffer)  // 从文件 test.txt 读取
}

接口实现

隐式实现

package main
 
import "fmt"
 
// 定义接口
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 3.14159 * c.Radius * c.Radius
}
 
func (c Circle) Perimeter() float64 {
    return 2 * 3.14159 * c.Radius
}
 
func main() {
    // 多态:不同的类型实现同一个接口
    var shape Shape
    
    shape = Rectangle{Width: 10, Height: 5}
    fmt.Printf("矩形面积: %.2f\n", shape.Area())
    
    shape = Circle{Radius: 5}
    fmt.Printf("圆形面积: %.2f\n", shape.Area())
    
    // 接口切片
    shapes := []Shape{
        Rectangle{Width: 10, Height: 5},
        Circle{Radius: 5},
    }
    
    for _, s := range shapes {
        fmt.Printf("面积: %.2f, 周长: %.2f\n", s.Area(), s.Perimeter())
    }
}

空接口

package main
 
import "fmt"
 
// 空接口:可以表示任何类型
type Any interface{}
 
func main() {
    // 空接口可以存储任何值
    var i interface{} = 42
    var s interface{} = "hello"
    var b interface{} = true
    
    fmt.Printf("整数: %v\n", i)
    fmt.Printf("字符串: %v\n", s)
    fmt.Printf("布尔: %v\n", b)
    
    // 类型断言
    if value, ok := i.(int); ok {
        fmt.Printf("i 是整数: %d\n", value)
    }
    
    // 类型 switch
    switch v := i.(type) {
    case int:
        fmt.Printf("整数: %d\n", v)
    case string:
        fmt.Printf("字符串: %s\n", v)
    default:
        fmt.Printf("未知类型: %T\n", v)
    }
    
    // 空接口切片(可以存储不同类型的值)
    var values []interface{} = []interface{}{
        42,
        "hello",
        true,
        3.14,
    }
    
    for _, v := range values {
        fmt.Printf("值: %v, 类型: %T\n", v, v)
    }
}

类型断言

package main
 
import "fmt"
 
func main() {
    var i interface{} = "hello"
    
    // 类型断言:检查接口值的具体类型
    // 方式一:两个返回值
    value, ok := i.(string)
    if ok {
        fmt.Printf("是字符串: %s\n", value)
    } else {
        fmt.Println("不是字符串")
    }
    
    // 方式二:一个返回值(如果类型不匹配会 panic)
    // value := i.(string)  // 如果 i 不是 string,会 panic
    
    // 类型断言用于接口转换
    type Writer interface {
        Write([]byte) (int, error)
    }
    
    type File struct {
        name string
    }
    
    func (f *File) Write(data []byte) (int, error) {
        fmt.Printf("写入: %s\n", string(data))
        return len(data), nil
    }
    
    var w Writer = &File{name: "test.txt"}
    
    // 断言为具体类型
    if file, ok := w.(*File); ok {
        fmt.Printf("文件名称: %s\n", file.name)
    }
}

接口组合

package main
 
import "fmt"
 
// 小接口原则:接口应该尽可能小
type Reader interface {
    Read([]byte) (int, error)
}
 
type Writer interface {
    Write([]byte) (int, 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(data []byte) (int, error) {
    fmt.Println("读取文件")
    return 0, nil
}
 
func (f *File) Write(data []byte) (int, error) {
    fmt.Println("写入文件")
    return 0, nil
}
 
func (f *File) Close() error {
    fmt.Println("关闭文件")
    return nil
}
 
func main() {
    var rwc ReadWriteCloser = &File{name: "test.txt"}
    rwc.Read(nil)
    rwc.Write(nil)
    rwc.Close()
}

接口值

package main
 
import "fmt"
 
type Shape interface {
    Area() float64
}
 
type Rectangle struct {
    Width  float64
    Height float64
}
 
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}
 
func main() {
    // 接口值包含两部分:
    // 1. 动态类型(具体类型)
    // 2. 动态值(具体值)
    
    var s Shape  // 接口值为 nil(类型和值都是 nil)
    fmt.Println(s == nil)  // true
    
    var r *Rectangle = nil
    s = r  // 接口值不为 nil(类型是 *Rectangle,值是 nil)
    fmt.Println(s == nil)  // false
    
    // 检查接口值是否为 nil
    if s != nil {
        fmt.Println("接口值不为 nil")
        // 但调用方法会 panic(因为值是 nil)
        // fmt.Println(s.Area())  // panic: nil pointer dereference
    }
    
    // 正确的 nil 检查
    rect := &Rectangle{Width: 10, Height: 5}
    s = rect
    if s != nil {
        fmt.Printf("面积: %.2f\n", s.Area())  // 50.00
    }
}

常见接口

fmt.Stringer

package main
 
import "fmt"
 
type Person struct {
    Name string
    Age  int
}
 
// 实现 Stringer 接口
func (p Person) String() string {
    return fmt.Sprintf("%s (%d 岁)", p.Name, p.Age)
}
 
func main() {
    person := Person{Name: "Alice", Age: 30}
    fmt.Println(person)  // Alice (30 岁)
    // fmt.Println 会自动调用 String() 方法
}

error 接口

package main
 
import (
    "errors"
    "fmt"
)
 
// 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 divide(a, b int) (int, error) {
    if b == 0 {
        return 0, &MyError{
            Code:    400,
            Message: "除数不能为 0",
        }
    }
    return a / b, nil
}
 
func main() {
    result, err := divide(10, 0)
    if err != nil {
        fmt.Printf("错误: %v\n", err)  // 错误: 错误代码 400: 除数不能为 0
        return
    }
    fmt.Printf("结果: %d\n", result)
    
    // 使用标准库的 errors
    err2 := errors.New("这是一个错误")
    fmt.Println(err2)
}

接口最佳实践

package main
 
import "fmt"
 
// 1. 小接口原则:接口应该尽可能小
// 好的接口:只包含必要的方法
type Reader interface {
    Read([]byte) (int, error)
}
 
// 不好的接口:包含太多方法
type BadInterface interface {
    Read([]byte) (int, error)
    Write([]byte) (int, error)
    Close() error
    Flush() error
    Seek(int64, int) (int64, error)
    // ... 太多方法
}
 
// 2. 接受接口,返回具体类型
// 函数参数使用接口
func ProcessData(r Reader) {
    // 处理数据
}
 
// 函数返回具体类型
func NewFile(name string) *File {
    return &File{name: name}
}
 
// 3. 接口隔离原则
type File struct {
    name string
}
 
func (f *File) Read(data []byte) (int, error) {
    return 0, nil
}
 
func (f *File) Write(data []byte) (int, error) {
    return 0, nil
}
 
func (f *File) Close() error {
    return nil
}
 
// 4. 使用接口实现依赖注入
type Database interface {
    Query(string) ([]string, error)
}
 
type MySQL struct{}
 
func (m *MySQL) Query(sql string) ([]string, error) {
    return []string{"result1", "result2"}, nil
}
 
type Service struct {
    db Database  // 依赖接口,而不是具体类型
}
 
func NewService(db Database) *Service {
    return &Service{db: db}
}
 
func (s *Service) GetData() {
    results, _ := s.db.Query("SELECT * FROM users")
    fmt.Println(results)
}
 
func main() {
    db := &MySQL{}
    service := NewService(db)  // 注入依赖
    service.GetData()
}

总结

Go 接口的特点:

  1. 隐式实现:不需要显式声明实现接口
  2. 小接口原则:接口应该尽可能小
  3. 组合:通过组合小接口创建大接口
  4. 空接口:可以表示任何类型
  5. 类型断言:检查接口值的具体类型
  6. 多态:不同的类型实现同一个接口

接口是 Go 实现多态和依赖注入的核心机制!


go 接口 多态 类型断言