// A Getter loads data for a key.
type Getter interface {
Get(key string) ([]byte, error)
}
// A GetterFunc implements Getter with a function.
type GetterFunc func(key string) ([]byte, error)
// Get implements Getter interface function
func (f GetterFunc) Get(key string) ([]byte, error) {
return f(key)
}接口型函数(Interface Function)(最典型的应用就是 Go 标准库 net/http 中的 http.HandlerFunc)。
我们可以把这段代码拆开,看看到底是怎么让“一个函数”去“实现接口”的:
1. 关键拆解:把函数定义成了一个“类型”
首先看这一行:
type GetterFunc func(key string) ([]byte, error)
Go 允许用 type 定义任何自定义类型。这里不是在定义普通函数,而是在**定义一个新的“自定义函数类型” GetterFunc**。
- 只要一个函数的参数是
(key string)、返回值是([]byte, error),它的签名就和GetterFunc完全一致。
2. 体现实现接口的地方:给函数类型绑定方法
真正的魔术在这一步:
func (f GetterFunc) Get(key string) ([]byte, error) {
return f(key)
}
按照 Go 的语法规则:
- 接口要求:
Getter接口要求有一个Get(key string) ([]byte, error)方法。 - 方法绑定:
func (f GetterFunc) Get(...)给自定义类型GetterFunc绑定了一个叫Get的方法。 - 满足接口:因为
GetterFunc拥有了Get方法,且方法签名与Getter接口定义的完全一致,所以GetterFunc这个类型隐式实现了Getter接口!
在 Get 方法内部,f(key) 就是直接调用 f 函数自己。
3. 为什么不直接用结构体实现?这样写有什么妙用?
如果不这么写,传统方式必须用结构体来实现接口:
传统方式(基于结构体):
必须先定义结构体,再写方法,调用时必须实例化结构体:
// 1. 定义结构体
type DBGetter struct{}
// 2. 实现接口
func (db DBGetter) Get(key string) ([]byte, error) {
return []byte("from db"), nil
}
// 3. 使用:必须传结构体实例
func GetFromSource(g Getter, key string) {
data, _ := g.Get(key)
fmt.Println(string(data))
}
GetFromSource(DBGetter{}, "some_key")
接口型函数方式(极简):
有了 GetterFunc 之后,你可以直接把一个匿名函数/闭包当成接口传进去,无需定义任何结构体!
// 只需要用 GetterFunc(...) 做一次显式类型转换:
GetFromSource(GetterFunc(func(key string) ([]byte, error) {
return []byte("from memory"), nil
}), "some_key")
4. 总结
- 谁实现了接口? → 是自定义函数类型
GetterFunc,而不是某个struct。 - 怎么体现的? →
GetterFunc绑定了符合Getter接口要求的Get方法。 - 核心价值:让调用方既可以传结构体对象,又可以方便地直接传一个匿名函数(只需用
GetterFunc()强转一下),极大地提升了 API 的灵活性。