Skip to content

接口和类型

接口定义

接口定义了一组方法的集合,任何实现了这些方法的类型都实现了该接口。

基本语法

go
type InterfaceName interface {
    Method1(param type) returnType
    Method2(param type) returnType
}

示例

go
// 定义接口
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 printArea(s Shape) {
    fmt.Printf("面积: %.2f\n", s.Area())
}

func main() {
    rect := Rectangle{Width: 10, Height: 5}
    circle := Circle{Radius: 5}
    
    printArea(rect)   // 面积: 50.00
    printArea(circle) // 面积: 78.54
}

接口实现

在 Go 中,接口的实现是隐式的。只要类型实现了接口中的所有方法,就自动实现了该接口。

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

// 任何实现了 Write 方法的类型都实现了 Writer 接口
type FileWriter struct {
    filename string
}

func (f FileWriter) Write(data []byte) (int, error) {
    // 实现写入逻辑
    return len(data), nil
}

接口组合

接口可以组合,形成更大的接口:

go
type Reader interface {
    Read([]byte) (int, error)
}

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

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

// 或者直接定义
type ReadWriter interface {
    Read([]byte) (int, error)
    Write([]byte) (int, error)
}

类型断言

类型断言用于检查接口值的具体类型。

基本用法

go
var i interface{} = "hello"

// 方式 1: 两个返回值
s, ok := i.(string)
if ok {
    fmt.Println("是字符串:", s)
}

// 方式 2: 一个返回值(如果失败会 panic)
s := i.(string)

Type Switch

go
func doSomething(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)
    default:
        fmt.Printf("未知类型: %T\n", v)
    }
}

空接口

空接口 interface{} 可以表示任何类型:

go
// 空接口
var i interface{}

i = 42
i = "hello"
i = true

// 使用空接口作为参数
func printValue(v interface{}) {
    fmt.Println(v)
}

类型别名

Go 1.18+ 引入了 any 作为 interface{} 的别名:

go
func printValue(v any) {
    fmt.Println(v)
}

接口的零值

接口的零值是 nil

go
var w Writer  // w 是 nil

接口值

接口值包含两部分:

  • 具体类型(动态类型)
  • 该类型的值(动态值)
go
var w Writer
w = FileWriter{filename: "test.txt"}
// w 的类型是 FileWriter,值是 FileWriter{filename: "test.txt"}

常见接口示例

io.Reader 和 io.Writer

go
import "io"

func copyData(r io.Reader, w io.Writer) error {
    data := make([]byte, 1024)
    for {
        n, err := r.Read(data)
        if n > 0 {
            w.Write(data[:n])
        }
        if err == io.EOF {
            break
        }
        if err != nil {
            return err
        }
    }
    return nil
}

fmt.Stringer

go
type Person struct {
    Name string
    Age  int
}

func (p Person) String() string {
    return fmt.Sprintf("%s (%d 岁)", p.Name, p.Age)
}

func main() {
    p := Person{Name: "Alice", Age: 30}
    fmt.Println(p)  // 自动调用 String() 方法
}

error 接口

go
type error interface {
    Error() string
}

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

func (e *MyError) Error() string {
    return e.Message
}

func doSomething() error {
    return &MyError{Message: "发生了错误"}
}

接口最佳实践

  1. 保持接口小巧:接口应该只包含必要的方法
  2. 接受接口,返回具体类型:函数参数使用接口,返回值使用具体类型
  3. 使用组合而非继承:通过接口组合实现复杂功能
go
// 好的设计:小巧的接口
type Reader interface {
    Read([]byte) (int, error)
}

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

// 通过组合实现复杂功能
type ReadWriter interface {
    Reader
    Writer
}

完整示例

go
package main

import "fmt"

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

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

func (d Dog) Speak() string {
    return fmt.Sprintf("%s 说: 汪汪", d.Name)
}

func (d Dog) Move() string {
    return fmt.Sprintf("%s 在跑", d.Name)
}

type Cat struct {
    Name string
}

func (c Cat) Speak() string {
    return fmt.Sprintf("%s 说: 喵喵", c.Name)
}

func (c Cat) Move() string {
    return fmt.Sprintf("%s 在走", c.Name)
}

// 使用接口
func describeAnimal(a Animal) {
    fmt.Println(a.Speak())
    fmt.Println(a.Move())
}

func main() {
    dog := Dog{Name: "旺财"}
    cat := Cat{Name: "咪咪"}
    
    describeAnimal(dog)
    describeAnimal(cat)
    
    // 类型断言
    var a Animal = Dog{Name: "小黑"}
    if dog, ok := a.(Dog); ok {
        fmt.Println("这是一只狗:", dog.Name)
    }
}

下一步

现在你已经掌握了 Go 语言的基础知识,接下来我们将学习:

基于 VitePress 构建