slava是我参与的一个github开源项目,主要的工作是构建一个高性能、K-V云数据库。 slava项目的连接
在项目中定义了一个函数类型:
type Consumer func(key string, val interface{}) bool
然后在一个具体的函数中使用到了该函数类型,该ForEach函数传入的参数是一个上面所定义的函数类型:
// 遍历节点
func (dict *ConcurrentDict) ForEach(consumer Consumer) {
if dict == nil {
panic("dict is nil")
}
for _, s := range dict.table {
s.mutex.RLock()
for key, value := range s.m {
b := consumer(key, value)
if !b {
break
}
}
s.mutex.RUnlock()
}
}
可能会让人疑惑不解的是,在ForEach函数中调用了consumer,也就是调用了我们定义的函数类型,但是函数类型的实现在哪实现了呢?
我们接着往下看,在接下来的Keys函数中调用了ForEach函数,如下:
func (dict *ConcurrentDict) Keys() []string {
if dict == nil {
panic("dict is nil")
}
i := 0
keys := make([]string, dict.Len())
dict.ForEach(func(key string, val interface{}) bool {
if i < len(keys) {
keys[i] = key
i++
} else {
keys = append(keys, key)
}
return true
})
return keys
}
consumer的具体实现在这个函数中,在调用dict.ForEach的时候传入了一个函数的实现,这个函数的传参和返回值和最一开始定义的Consumer函数类型一样。并且ForEach函数的传参传入的时候我们定义的Consumer函数,所以上面的函数在调用dict.ForEach的时候实现了一个Consumer函数的实体。
1.在分析type定义函数类型的之前,我们先来了解一下什么是函数类型。
函数类型又叫函数签名 , 一个函数的类型就是函数定义首行去掉函数名、参数名和大括号,可以 使用 fmt.Printf 的”%T”格式化参数打印函数的类型。
package main
import "fmt"
func add(a int, b int) int {
return a + b
}
func diff(a int, b int) int {
return a - b
}
func printStr1(str string) {
fmt.Println(str)
}
func printStr2(s string) {
s2 := "abc"
fmt.Println(s + s2)
}
func main() {
fmt.Printf("%T\n", add)
fmt.Printf("%T\n", diff)
fmt.Println("--------------------")
fmt.Printf("%T\n", printStr1)
fmt.Printf("%T\n", printStr2)
}
从上面的代码的运行结果可知,定义的add和diff属于同一种函数类型,而printStr1和printStr2属于另一种函数类型,函数的类型只和函数的传参类型、返回值类型以及他们的个数有关,和函数名以及函数的具体实现无关。
2.type函数定义函数类型
在Go语言中type可以定义自定义的类型,比如type TreeNode struct {},type Name string ,也可以定义定义函数类型,比如 type fun func (a int) int ,这里自定义定义了一个叫fun的函数类型的变量,注意:fun是一个变量不是一个函数。既然是一个变量,那么相同底层类型的变量之间是可以相互转换的。
(1)函数类型可以定义自己的函数
type handler func(a int, b int) int
func (h handler) AddIsTrue(x int) bool {
a := x * 10
b := x / 10
return h(a, b) == x
}
(2)底层是相同类型可以相互转换
type handler func(a int, b int) int
func (h handler) AddIsTrue(x int) bool {
a := x * 10
b := x / 10
return h(a, b) == x
}
func main() {
// handler 和 add函数属于同一种类型
var a handler = add
a.AddIsTrue(10)
//
var b handler = func(a int, b int) int {
x := a * b
return x
}
}
在调用函数的时候传入函数的实现,与文章一开始中说的Keys函数调用ForEach函数的时候实现函数类型的情况类似
// 定义一个函数类型
type handler func(a int, b int) int
// 在该函数的传参中传入了函数类型变量
func fun(h handler, a int, b int) int {
return h(a, b)
}
func main() {
// 调用fun函数,并且在fun函数的传参中传入了和handler类型一样的函数
c := fun(func(a int, b int) int {
x := a / b
return x
}, 40, 20)
fmt.Println(c)
// 上面的代码可以改写成下面的代码
//var h handler = func(a int, b int) int {
// x := a / b
// return x
//}
//c := fun(h, 40, 20)
//fmt.Println(c)
}
3.总结
利用type字段可以自定义类型,定义出来的是一个变量,具有变量的性质,相同底层类型的变量之间是可以相互转换。