文章

接口与反射

接口与反射

接口基础

隐式实现

// Go 的接口是隐式实现的:不需要 implements 关键字
// 只要类型实现了接口定义的所有方法,就自动满足该接口

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

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

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

// File 类型隐式实现了 ReadWriter
type File struct { /* ... */ }

func (f *File) Read(p []byte) (n int, err error)  { /* ... */ return }
func (f *File) Write(p []byte) (n int, err error) { /* ... */ return }

// File 自动满足 ReadWriter 接口
var rw ReadWriter = &File{}

接口的内部结构

// interface 底层是一个双字结构
type iface struct {
    tab  *itab          // 类型信息表(类型 + 方法表)
    data unsafe.Pointer // 指向具体值
}

// nil interface vs nil value
var r io.Reader         // tab=nil, data=nil → r == nil
var r io.Reader = (*bytes.Buffer)(nil)  // tab!=nil, data=nil → r != nil !

经典陷阱:接口值不等于 nil,即使底层值为 nil

// ❌ 常见 bug
func getUser() (*User, error) {
    var u *User  // nil 指针
    return u, nil
}

// 调用方
u, err := getUser()
if err != nil {
    // 不会进来(err 是 nil)
}

// 更隐蔽的版本
func wrapError(err error) error {
    if err == nil {
        return nil
    }
    return fmt.Errorf("wrapped: %w", err)
}

// 这个函数对 nil error 会返回非 nil error!
// 因为 fmt.Errorf 返回的是 *wrapError,它的接口值 (tab, data) 中 data=nil 但 tab!=nil

空接口 interface{}

// Go 1.18+ 之前:interface{} 表示任意类型
// Go 1.18+:any 是 interface{} 的别名
var v any = 42
v = "hello"
v = []int{1, 2, 3}

// type switch 处理
func describe(v any) {
    switch v := v.(type) {
    case int:
        fmt.Printf("int: %d\n", v)
    case string:
        fmt.Printf("string: %s\n", v)
    case []int:
        fmt.Printf("[]int: %v\n", v)
    default:
        fmt.Printf("unknown: %T\n", v)
    }
}

接口设计原则

1. 接口越小越好

// ✓ 好:小接口(io.Reader 只有 1 个方法)
type Reader interface {
    Read(p []byte) (n int, err error)
}

// ✓ 好:标准库的小接口
type Stringer interface {
    String() string
}
type Closer interface {
    Close() error
}
type Error interface {
    Error() string
}

// ❌ 不好:大接口(违反接口隔离原则)
type UserService interface {
    GetByID(id string) (*User, error)
    Create(user *User) error
    Update(user *User) error
    Delete(id string) error
    List(page, size int) ([]*User, error)
    Search(query string) ([]*User, error)
    Export(format string) ([]byte, error)
    Import(data []byte) error
    SendEmail(id string, subject, body string) error
    ResetPassword(id string) error
}

// ✓ 好:按职责拆分
type UserReader interface {
    GetByID(id string) (*User, error)
    List(page, size int) ([]*User, error)
    Search(query string) ([]*User, error)
}

type UserWriter interface {
    Create(user *User) error
    Update(user *User) error
    Delete(id string) error
}

type UserReadWriter interface {
    UserReader
    UserWriter
}

2. 消费者定义接口

// ✓ 正确:在消费者侧定义接口(只声明需要的方法)
// service/user_service.go
type UserRepository interface {
    FindByID(ctx context.Context, id string) (*entity.User, error)
}

type UserService struct {
    repo UserRepository  // 依赖接口,不依赖具体实现
}

// repository/user_repo.go(不需要知道接口的存在)
type UserRepository struct {
    db *gorm.DB
}
func (r *UserRepository) FindByID(ctx context.Context, id string) (*entity.User, error) {
    // ...
}

// ❌ 错误:在实现侧定义接口
// repository/user_repo.go
// type UserRepository interface { ... }  // 不要在实现包定义接口

3. 常见标准库接口

接口方法用途
io.ReaderioRead(p []byte) (n int, err error)读取数据
io.WriterioWrite(p []byte) (n int, err error)写入数据
io.CloserioClose() error关闭资源
fmt.StringerfmtString() string字符串表示
errorbuiltinError() string错误
sort.InterfacesortLen(), Less(), Swap()排序
json.Marshalerencoding/jsonMarshalJSON() ([]byte, error)自定义 JSON 序列化
json.Unmarshalerencoding/jsonUnmarshalJSON([]byte) error自定义 JSON 反序列化
driver.Valuerdatabase/sqlValue() (driver.Value, error)自定义 SQL 值
sql.Scannerdatabase/sqlScan(src interface{}) error自定义 SQL 扫描
http.Handlernet/httpServeHTTP(w, r)HTTP 处理器
http.HandlerFuncnet/httpfunc(http.ResponseWriter, *http.Request)函数适配器

类型断言与 type switch

// 类型断言
var i interface{} = "hello"

s, ok := i.(string)  // s="hello", ok=true
n, ok := i.(int)     // n=0, ok=false

// type switch
switch v := i.(type) {
case string:
    fmt.Println("string, len:", len(v))
case int:
    fmt.Println("int, value:", v)
case fmt.Stringer:
    fmt.Println("Stringer:", v.String())
default:
    fmt.Printf("unknown type %T\n", v)
}

// 接口到接口的断言
var r io.Reader = strings.NewReader("hello")
rw, ok := r.(io.Writer)  // strings.Reader 没实现 Writer,ok=false

反射 reflect

原则:能不用反射就不用。反射代码可读性差、性能低、不安全(运行时才报错)。

reflect.Type vs reflect.Value

import "reflect"

type User struct {
    Name string `json:"name" validate:"required"`
    Age  int    `json:"age" validate:"min=0"`
}

u := User{Name: "小徐", Age: 28}

// 获取 Type
t := reflect.TypeOf(u)
fmt.Println(t.Name())        // User
fmt.Println(t.Kind())        // struct
fmt.Println(t.NumField())    // 2

// 获取 Value
v := reflect.ValueOf(u)
fmt.Println(v.Kind())        // struct

// 遍历字段
for i := 0; i < t.NumField(); i++ {
    field := t.Field(i)
    value := v.Field(i)
    tag := field.Tag.Get("json")
    fmt.Printf("%s %s = %v (json:%s)\n",
        field.Name, field.Type, value.Interface(), tag)
}
// 输出:
// Name string = 小徐 (json:name)
// Age int = 28 (json:age)

修改值(需要指针)

u := User{Name: "小徐", Age: 28}

// ❌ 反射值不可寻址
v := reflect.ValueOf(u)
// v.FieldByName("Name").SetString("小王")  // panic: not addressable

// ✓ 通过指针修改
v = reflect.ValueOf(&u).Elem()  // Elem() 解引用
v.FieldByName("Name").SetString("小王")
v.FieldByName("Age").SetInt(30)
fmt.Println(u)  // {小王 30}

动态调用方法

type Calculator struct{}

func (c Calculator) Add(a, b int) int { return a + b }

c := Calculator{}
v := reflect.ValueOf(c)

// 获取方法
method := v.MethodByName("Add")

// 构造参数
args := []reflect.Value{
    reflect.ValueOf(3),
    reflect.ValueOf(5),
}

// 调用
results := method.Call(args)
fmt.Println(results[0].Int())  // 8

反射性能对比

操作直接调用反射调用性能比
字段读取~1ns~50ns50x
字段设置~1ns~80ns80x
方法调用~2ns~200ns100x

反射实际应用

func validateStruct(s interface{}) error {
    v := reflect.ValueOf(s)
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }
    t := v.Type()

    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        value := v.Field(i)

        validateTag := field.Tag.Get("validate")
        if validateTag == "" {
            continue
        }

        rules := strings.Split(validateTag, ",")
        for _, rule := range rules {
            if err := applyRule(field.Name, value, rule); err != nil {
                return err
            }
        }
    }
    return nil
}

// 2. 通用深拷贝
func DeepCopy(src interface{}) interface{} {
    if src == nil {
        return nil
    }
    v := reflect.ValueOf(src)
    return deepCopyValue(v).Interface()
}

func deepCopyValue(v reflect.Value) reflect.Value {
    switch v.Kind() {
    case reflect.Ptr:
        if v.IsNil() {
            return reflect.Zero(v.Type())
        }
        copy := reflect.New(v.Elem().Type())
        copy.Elem().Set(deepCopyValue(v.Elem()))
        return copy
    case reflect.Slice:
        if v.IsNil() {
            return reflect.Zero(v.Type())
        }
        copy := reflect.MakeSlice(v.Type(), v.Len(), v.Len())
        for i := 0; i < v.Len(); i++ {
            copy.Index(i).Set(deepCopyValue(v.Index(i)))
        }
        return copy
    case reflect.Map:
        if v.IsNil() {
            return reflect.Zero(v.Type())
        }
        copy := reflect.MakeMap(v.Type())
        iter := v.MapRange()
        for iter.Next() {
            copy.SetMapIndex(iter.Key(), deepCopyValue(iter.Value()))
        }
        return copy
    default:
        return v
    }
}

// 3. 结构体转 map
func StructToMap(s interface{}) map[string]interface{} {
    result := make(map[string]interface{})
    v := reflect.ValueOf(s)
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }
    t := v.Type()

    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        jsonTag := field.Tag.Get("json")
        if jsonTag == "" || jsonTag == "-" {
            continue
        }
        // 处理 omitempty 等选项
        name := strings.Split(jsonTag, ",")[0]
        result[name] = v.Field(i).Interface()
    }
    return result
}

何时用反射

场景是否用反射替代方案
ORM 映射代码生成(codegen)
JSON 序列化✓(标准库已实现)直接用 encoding/json
配置解析✓(viper 等)mapstructure
验证器go-playground/validator
依赖注入代码生成(wire)
业务逻辑接口 + 类型断言
性能敏感路径直接调用
简单类型判断type switch