因看不懂 go 中的自定义函数类型,看了https://www.jianshu.com/p/431abe0d2ed5 理解了不少,特此搬运到自己博客
在看golang 的http服务部分代码时,被golang 中的 type func()写法难住了,一时没看懂代码。后来查资料后,有了一点理解。
在golang中可以通过这样简单实现一个http服务
package main
import "net/http"
func mHttp() {
http.HandleFunc("/", h)
http.ListenAndServe("0.0.0.0:8888",nil)
}
func h(w http.ResponseWriter, r *http.Request) {
}
http.HandleFunc()
是一个注册函数,传一个string类型的路由,和一个函数,函数的参数为(http.ResponseWriter, *http.Request)。跟踪进入函数,在golang 源码net/http/server.go文件中
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
在HandleFunc调用了DefaultServeMux.HandleFunc(pattern, handler)
至于这些函数是干啥的先不做探讨,这不是本文的重点。
再次跟进函数
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}
在mux.Handle(pattern, HandlerFunc(handler)) 的第二个参数HandlerFunc(handler)是什么鬼。
跟进看一下
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
原来HandlerFunc 是用 type 定义的函数,而函数的类型就是最开始传入的类型func(ResponseWriter, *Request)
ServeHTTP是HandlerFunc的一个方法(注意一下,golang中方法和函数不是一回事)。并且HandlerFunc实现了 Handler接口
Handler接口定义:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
回到HandleFunc方法中,mux.Handle(pattern, HandlerFunc(handler))
的第二个参数是把传入的函数 handler 强转成 HandlerFunc类型,这样handler就实现了Handler接口。
到这我们明白HandlerFunc(handler)
是把普通函数强转成type定义的函数。
现在写一个简单的demo验证一下:
package main
import "fmt"
type CalculateType func(int, int) // 声明了一个函数类型
// 该函数类型实现了一个方法
func (c CalculateType) Serve() {
fmt.Println("我是一个函数类型")
}
// 加法函数
func add(a, b int) {
fmt.Println(a + b)
}
// 乘法函数
func mul(a, b int) {
fmt.Println(a * b)
}
func main() {
a := CalculateType(add) // 将add函数强制转换成CalculateType类型
b := CalculateType(mul) // 将mul函数强制转换成CalculateType类型
a(2, 3)
b(2, 3)
a.Serve()
b.Serve()
}
// 5
// 6
// 我是一个函数类型
// 我是一个函数类型
如上,声明了一个 CalculateType 函数类型,并实现 Serve() 方法,并将拥有相同参数的 add 和 mul 强制转换成 CalculateType 函数类型,同时这两个函数都拥有了 CalculateType 函数类型的 Serve() 方法。
可以改写成这样
package main
import "fmt"
type CalculateType func(int, int) // 声明了一个函数类型
// 该函数类型实现了一个方法
func (c CalculateType) Serve(a, b int) {
c(a, b)
fmt.Println("我是一个函数类型")
}
// 加法函数
func add(a, b int) {
fmt.Println(a + b)
}
// 乘法函数
func mul(a, b int) {
fmt.Println(a * b)
}
func main() {
a := CalculateType(add) // 将add函数强制转换成CalculateType类型
b := CalculateType(mul) // 将mul函数强制转换成CalculateType类型
a.Serve(2, 3)
b.Serve(2, 3)
}
// 5
// 我是一个函数类型
// 6
// 我是一个函数类型
再写一个例子我们来理解为什么要用type function
1.原始结构体实现接口
package main
import "fmt"
type Handler interface { //定义一个处理器接口,用来做处理
Do(k, v string)
}
//定义一个Each函数,接受Handler处理器的一个实例和map,把map遍历出来给Do
//然后做什么就取决于h这个handler处理器了
func Each(m map[string]string, h Handler) {
if m != nil && len(m) > 0 {
for k, v := range m {
h.Do(k, v)
}
}
}
type weclome string //定义一个weclome类型
//weclome类型实现了Handler处理器接口,他用来做自我介绍
func (w weclome) Do(k, v string) {
// fmt.Println(w, ",我叫:", k, "今年", v, "岁")
fmt.Printf("%s,我叫%s,今年%s岁\n", w, k, v)
}
func main() {
people := make(map[string]string)
people["张三"] = "18"
people["李四"] = "19"
people["小明"] = "20"
var w weclome
w = "大家好"
Each(people, w)
}
# 大家好,我叫张三,今年18岁
# 大家好,我叫李四,今年19岁
# 大家好,我叫小明,今年20岁
以上实现,主要有两点不太好:
- 因为必须要实现Handler接口,Do这个方法名不能修改,不能定义一个更有意义的名字
- 必须要新定义一个类型,才可以实现Handler接口,才能使用Each函数
2.接口型函数出场
首先我们先解决第一个问题,根据我们具体做的事情定义一个更有意义的方法名,比如例子中是自我介绍,那么使用selfInfo要比Do这个干巴巴的方法要好的多。
package main
import "fmt"
type Handler interface { //定义一个处理器接口,用来做处理
Do(k, v string)
}
type HandlerFunc func(k, v string)
func (h HandlerFunc) Do(k, v string) {
h(k, v)
}
//定义一个Each函数,接受HandlerFunc处理器的一个实例和map,把map遍历出来给Do
//然后做什么就取决于h这个HandlerFunc处理器了
func Each(m map[string]string, h HandlerFunc) {
if m != nil && len(m) > 0 {
for k, v := range m {
h.Do(k, v)
}
}
}
func selfinfo(k, v string) {
fmt.Printf("大家好,我叫%s,今年%s岁\n", k, v)
}
func selfinfo1(k, v string) {
fmt.Printf("大家好,接下来上台的是%s,今年%s岁\n", k, v)
}
func main() {
people := make(map[string]string)
people["张三"] = "18"
people["李四"] = "19"
people["小明"] = "20"
Each(people, HandlerFunc(selfinfo))
Each(people, HandlerFunc(selfinfo1))
}
# 大家好,我叫小明,今年20岁
# 大家好,我叫张三,今年18岁
# 大家好,我叫李四,今年19岁
# 大家好,接下来上台的是李四,今年19岁
# 大家好,接下来上台的是小明,今年20岁
# 大家好,接下来上台的是张三,今年18岁
不同的场景可以用不同的函数,但是每次强制转型不太好,我们继续重构,可以采用新定义一个函数的方式,帮助调用者强制转型。
func EachFunc(m map[string]string, f func(k, v string)) {
Each(m, HandlerFunc(f))
}
现在我们发现EachFunc函数接收的是一个func(k, v string)类型的函数,没有必要实现Handler接口了
func main() {
people := make(map[string]string)
people["张三"] = "18"
people["李四"] = "19"
people["小明"] = "20"
// Each(people, HandlerFunc(selfinfo))
// Each(people, HandlerFunc(selfinfo1))
EachFunc(people, selfinfo)
}
# 大家好,我叫小明,今年20岁
# 大家好,我叫张三,今年18岁
# 大家好,我叫李四,今年19岁
大体的type function的用法,可以理解一下,真的是脑子要想爆炸了