golang中函數類型


今天看Martini文檔,其功能列表提到完全兼容http.HandlerFunc接口,就去查閱了Go: net/http的文檔,看到type HandlerFunc這部分,頓時蒙圈了。由於之前學習的時候沒有關注過function types的知識點,就Google了一些文章,才算是有了個大概的了解。

從golang的官方文檔得知function types的解釋是這樣的。

A function type denotes the set of all functions with the same parameter and result types.

先找個例子來看一下:

package main import "fmt"

// Greeting function types type Greeting func(name string) string func say(g Greeting, n string) { fmt.Println(g(n)) } func english(name string) string { return "Hello, " + name } func main() { say(english, "World") } 輸出Hello, World

 

 

say()函數要求傳入一個Greeting類型,因為english函數的參數和返回值跟Greeting一樣,參考接口的概念這里可以做類型轉換。我們換個方式來實現上面的功能:

package main import "fmt"

// Greeting function types type Greeting func(name string) string func (g Greeting) say(n string) { fmt.Println(g(n)) } func english(name string) string { return "Hello, " + name } func main() { g := Greeting(english) g.say("World") }

 

同樣輸出Hello, World,只是給Greeting類型添加了say()方法。上面說了,函數類型是表示所有包含相同參數和返回類型的函數集合。我們在一開始先把func(name string) string這樣的函數聲明成Greeting類型,接着我們通過Greeting(english)english函數轉換成Greeting類型。通過這個轉換以后,我們就可以借由變量g調用Greeting類型的say()方法。兩段代碼的差異就是go的類型系統添加方法和類C++語言添加類型方法的差異,具體講解可以去查看《Go語言編程》第3章為類型添加方法這一節。

既然是函數集合,那么只有一個函數顯然是不足以說明問題的。

package main import "fmt"

// Greeting function types type Greeting func(name string) string func (g Greeting) say(n string) { fmt.Println(g(n)) } func english(name string) string { return "Hello, " + name } func french(name string) string { return "Bonjour, " + name } func main() { g := Greeting(english) g.say("World") g = Greeting(french) g.say("World") } 輸出 Hello, World Bonjour, World

 

在其他語言里面,有些函數可以直接作為參數傳遞,有些是以函數指針進行傳遞,但是都沒有辦法像go這樣可以給函數類型“增加”新方法。

回到Go: net/http的HandlerFunc類型,只要Martini的函數遵循文檔中type HandlerFunc func(ResponseWriter, *Request)的要求,就可以轉換成HandlerFunc類型,也就可以調用func (HandlerFunc)ServeHTTP函數。

在 Go 語言中,我們可以把函數作為一種變量,用 type 去定義它,那么這個函數類型就可以作為值傳遞,甚至可以實現方法,這一特性是在太靈活了,有時候我們甚至可以利用這一特性進行類型轉換。作為值傳遞的條件是類型具有相同的參數以及相同的返回值。
函數的類型轉換

Go 語言的類型轉換基本格式如下:

type_name(expression)
復制代碼

舉個例子:

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(a, b int) int // 聲明了一個函數類型

// 加法函數
func add(a, b int) int {
  return a + b
}

// 乘法函數
func mul(a, b int) int {
  return a * b
}

func Calculate(a, b int, f CalculateType) int {
  return f(a, b)
}

func main() {
  a, b := 2, 3
  fmt.Println(Calculate(a, b, add))
  fmt.Println(Calculate(a, b, mul))
}
// 5
// 6
復制代碼

如上例子,Calculate 的 f 參數類型為 CalculateType,add 和 mul 函數具有和 CalculateType 函數類型相同的參數和返回值,因此可以將 add 和 mul 函數作為參數傳入 Calculate 函數中。
net/http 包源碼例子

// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
  DefaultServeMux.HandleFunc(pattern, handler)
}
復制代碼

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
  mux.Handle(pattern, HandlerFunc(handler))
}
復制代碼

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
  f(w, r)
}
復制代碼

剛開始看到這段源碼的時候,真的有點懵逼了,這段源碼的目的是為了將我們的 Handler 強制實現 ServeHTTP() 方法,如下例子:

func sayHi(w http.ResponseWriter, r *http.Request) {
  io.WriteString(w, "hi")
}

func main() {
  http.HandlerFunc("/", sayHi)
  http.ListenAndserve(":8080", nil)
}
復制代碼

因為 HandlerFunc 是一個函數類型,而 sayHi 函數擁有和 HandlerFunc 函數類型一樣的參數值,因此可以將 sayHi 強制轉換成 HandlerFunc,因此 sayHi 也擁有了 ServeHTTP() 方法,也就實現了 Handler 接口,同時,HandlerFunc 的 ServeHTTP 方法執行了它自己本身,也就是 sayHi 函數,這也就可以看出來了,sayHi 就是 Handler 被調用之后的執行結果。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM