C語言中文網 目錄
首頁 > Go語言教程 > Go語言函數 閱讀:3,774

Go語言函數類型實現接口——把函數作為接口來調用

函數和其他類型一樣都屬于“一等公民”,其他類型能夠實現接口,函數也可以,本節將分別對比結構體與函數實現接口的過程。

首先給出本節完整的代碼:
package main

import (
    "fmt"
)

// 調用器接口
type Invoker interface {
    // 需要實現一個Call方法
    Call(interface{})
}

// 結構體類型
type Struct struct {
}

// 實現Invoker的Call
func (s *Struct) Call(p interface{}) {
    fmt.Println("from struct", p)
}

// 函數定義為類型
type FuncCaller func(interface{})

// 實現Invoker的Call
func (f FuncCaller) Call(p interface{}) {

    // 調用f函數本體
    f(p)
}

func main() {

    // 聲明接口變量
    var invoker Invoker

    // 實例化結構體
    s := new(Struct)

    // 將實例化的結構體賦值到接口
    invoker = s

    // 使用接口調用實例化結構體的方法Struct.Call
    invoker.Call("hello")

    // 將匿名函數轉為FuncCaller類型,再賦值給接口
    invoker = FuncCaller(func(v interface{}) {
        fmt.Println("from function", v)
    })

    // 使用接口調用FuncCaller.Call,內部會調用函數本體
    invoker.Call("hello")
}

有如下一個接口:
// 調用器接口
type Invoker interface {
    // 需要實現一個Call()方法
    Call(interface{})
}
這個接口需要實現 Call() 方法,調用時會傳入一個 interface{} 類型的變量,這種類型的變量表示任意類型的值。

接下來,使用結構體進行接口實現。

結構體實現接口

結構體實現 Invoker 接口的代碼如下:
// 結構體類型
type Struct struct {
}

// 實現Invoker的Call
func (s *Struct) Call(p interface{}) {
    fmt.Println("from struct", p)
}
代碼說明如下:
  • 第 2 行,定義結構體,該例子中的結構體無須任何成員,主要展示實現 Invoker 的方法。
  • 第 6 行,Call() 為結構體的方法,該方法的功能是打印 from struct 和傳入的 interface{} 類型的值。

將定義的 Struct 類型實例化,并傳入接口中進行調用,代碼如下:
// 聲明接口變量
var invoker Invoker

// 實例化結構體
s := new(Struct)

// 將實例化的結構體賦值到接口
invoker = s

// 使用接口調用實例化結構體的方法Struct.Call
invoker.Call("hello")
代碼說明如下:
  • 第 2 行,聲明 Invoker 類型的變量。
  • 第 5 行,使用 new 將結構體實例化,此行也可以寫為 s:=&Struct。
  • 第 8 行,s 類型為 *Struct,已經實現了 Invoker 接口類型,因此賦值給 invoker 時是成功的。
  • 第 11 行,通過接口的 Call() 方法,傳入 hello,此時將調用 Struct 結構體的 Call() 方法。

接下來,對比下函數實現結構體的差異。

代碼輸出如下:
from struct hello

函數體實現接口

函數的聲明不能直接實現接口,需要將函數定義為類型后,使用類型實現結構體。當類型方法被調用時,還需要調用函數本體。
// 函數定義為類型
type FuncCaller func(interface{})

// 實現Invoker的Call
func (f FuncCaller) Call(p interface{}) {

    // 調用f()函數本體
    f(p)
}
代碼說明如下:
  • 第 2 行,將 func(interface{}) 定義為 FuncCaller 類型。
  • 第 5 行,FuncCaller 的 Call() 方法將實現 Invoker 的 Call() 方法。
  • 第 8 行,FuncCaller 的 Call() 方法被調用與 func(interface{}) 無關,還需要手動調用函數本體。

上面代碼只是定義了函數類型,需要函數本身進行邏輯處理。FuncCaller 無須被實例化,只需要將函數轉換為 FuncCaller 類型即可,函數來源可以是命名函數、匿名函數或閉包,參見下面代碼:
// 聲明接口變量
var invoker Invoker

// 將匿名函數轉為FuncCaller類型, 再賦值給接口
invoker = FuncCaller(func(v interface{}) {
    fmt.Println("from function", v)
})

// 使用接口調用FuncCaller.Call, 內部會調用函數本體
invoker.Call("hello")
代碼說明如下:
  • 第 2 行,聲明接口變量。
  • 第 5 行,將 func(v interface{}){} 匿名函數轉換為 FuncCaller 類型(函數簽名才能轉換),此時 FuncCaller 類型實現了 Invoker 的 Call() 方法,賦值給 invoker 接口是成功的。
  • 第 10 行,使用接口方法調用。

代碼輸出如下:
from function hello

HTTP包中的例子

HTTP 包中包含有 Handler 接口定義,代碼如下:
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
Handler 用于定義每個 HTTP 的請求和響應的處理過程。

同時,也可以使用處理函數實現接口,定義如下:
type HandlerFunc func(ResponseWriter, *Request)

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}
要使用閉包實現默認的 HTTP 請求處理,可以使用 http.HandleFunc() 函數,函數定義如下:
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
}
而 DefaultServeMux 是 ServeMux 結構,擁有 HandleFunc() 方法,定義如下:
func (mux *ServeMux) HandleFunc(pattern string, handler func
(ResponseWriter, *Request)) {
    mux.Handle(pattern, HandlerFunc(handler))
}
上面代碼將外部傳入的函數 handler() 轉為 HandlerFunc 類型,HandlerFunc 類型實現了 Handler 的 ServeHTTP 方法,底層可以同時使用各種類型來實現 Handler 接口進行處理。

精美而實用的網站,提供C語言C++STLLinuxShellJavaGo語言等教程,以及socketGCCviSwing設計模式JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo