跳到主要内容

Interface(接口)

接口描述行为契约。Go 采用隐式满足:类型拥有接口要求的全部方法,就自动实现接口。接口通常应由使用方定义,并保持小而专一。

最小接口

package main

import "fmt"

type Stringer interface{ String() string }
type User struct{ Name string }

func (u User) String() string { return u.Name }
func printValue(value Stringer) { fmt.Println(value.String()) }

func main() { printValue(User{Name: "Ada"}) }

接口只要求方法,不要求实现者声明 implements。多个小接口可以通过嵌入组合;不要为了复刻一个具体类型的全部方法而定义大接口。

接口值与 typed nil

接口值可以理解为“动态类型 + 动态值”。只有两者都为空时接口才等于 nil

var pointer *User
var value Stringer = pointer
fmt.Println(value == nil) // false:动态类型是 *User。

如果 *User 的方法会解引用接收者,调用 value.String() 可能 panic。返回接口时不要把 typed nil 装进接口;需要表达缺失时优先返回明确的 nil 指针或额外的 error/布尔值。

类型断言与类型分支

单次断言使用 comma-ok 形式避免失败 panic:

func describe(value any) string {
text, ok := value.(string)
if ok {
return "text: " + text
}
return fmt.Sprintf("kind: %T", value)
}

需要处理多种动态类型时使用类型 switch,并为未知类型保留默认分支。断言是边界适配手段,不应替代稳定的多态接口。

接口组合与约束边界

type Reader interface{ Read([]byte) (int, error) }
type Closer interface{ Close() error }
type ReadCloser interface {
Reader
Closer
}

普通接口值只能描述方法;包含类型集合(例如 int | string)的接口只能作为泛型约束,不能声明普通变量。anyinterface{} 的别名,只保证“没有方法要求”,不保证可比较、可排序或可 JSON 校验。

常见错误

  • 把接口当作任意值容器,丢失静态类型检查和输入校验。
  • 忽略 typed nil,误以为接口已经是 nil
  • 定义过大的接口,导致实现和测试困难。
  • 失败断言使用单值形式,造成运行时 panic。
  • 用类型断言代替稳定的多态行为。

TypeScript 的 interface 主要在编译期描述对象形状;Go 接口值存在运行时动态类型和值,且接口只描述方法,不等价于 TypeScript 的属性类型。

小结

  • Go 接口通过方法集隐式满足。
  • 接口值的动态类型和值共同决定 nil 和比较行为。
  • 小接口便于替换和组合;断言应集中在边界适配处。