C語言中文網 目錄
首頁 > Go語言教程 > Go語言接口 閱讀:3,572

Go語言實現接口的條件

接口定義后,需要實現接口,調用方才能正確編譯通過并使用接口。接口的實現需要遵循兩條規則才能讓接口可用。

接口被實現的條件一:接口的方法與實現接口的類型方法格式一致

在類型中添加與接口簽名一致的方法就可以實現該方法。簽名包括方法中的名稱、參數列表、返回參數列表。也就是說,只要實現接口類型中的方法的名稱、參數列表、返回參數列表中的任意一項與接口要實現的方法不一致,那么接口的這個方法就不會被實現。

為了抽象數據寫入的過程,定義 DataWriter 接口來描述數據寫入需要實現的方法,接口中的 WriteData() 方法表示將數據寫入,寫入方無須關心寫入到哪里。實現接口的類型實現 WriteData 方法時,會具體編寫將數據寫入到什么結構中。這里使用file結構體實現 DataWriter 接口的 WriteData 方法,方法內部只是打印一個日志,表示有數據寫入,詳細實現過程請參考下面的代碼。

數據寫入器的抽象:
package main

import (
    "fmt"
)

// 定義一個數據寫入器
type DataWriter interface {
    WriteData(data interface{}) error
}

// 定義文件結構,用于實現DataWriter
type file struct {
}

// 實現DataWriter接口的WriteData方法
func (d *file) WriteData(data interface{}) error {

    // 模擬寫入數據
    fmt.Println("WriteData:", data)
    return nil
}

func main() {

    // 實例化file
    f := new(file)

    // 聲明一個DataWriter的接口
    var writer DataWriter

    // 將接口賦值f,也就是*file類型
    writer = f

    // 使用DataWriter接口進行數據寫入
    writer.WriteData("data")
}
代碼說明如下:
  • 第 8 行,定義 DataWriter 接口。這個接口只有一個方法,即 WriteData(),輸入一個 interface{} 類型的 data,返回一個 error 結構表示可能發生的錯誤。
  • 第 17 行,file 的 WriteData() 方法使用指針接收器。輸入一個 interface{} 類型的 data,返回 error。
  • 第 27 行,實例化 file 賦值給 f,f 的類型為 *file。
  • 第 30 行,聲明 DataWriter 類型的 writer 接口變量。
  • 第 33 行,將 *file 類型的 f 賦值給 DataWriter 接口的 writer,雖然兩個變量類型不一致。但是 writer 是一個接口,且 f 已經完全實現了 DataWriter() 的所有方法,因此賦值是成功的。
  • 第 36 行,DataWriter 接口類型的 writer 使用 WriteData() 方法寫入一個字符串。

運行代碼,輸出如下:
WriteData: data

本例中調用及實現關系如下圖所示。

圖:WriteWriter的實現過程

當類型無法實現接口時,編譯器會報錯,下面列出常見的幾種接口無法實現的錯誤。

1) 函數名不一致導致的報錯

在以上代碼的基礎上嘗試修改部分代碼,造成編譯錯誤,通過編譯器的報錯理解如何實現接口的方法。首先,修改 file 結構的 WriteData() 方法名,將這個方法簽名(第17行)修改為:
func (d *file) WriteDataX(data interface{}) error {
編譯代碼,報錯:

cannot use f (type *file) as type DataWriter in assignment:
        *file does not implement DataWriter (missing WriteData method)

報錯的位置在第 33 行。報錯含義是:不能將 f 變量(類型*file)視為 DataWriter 進行賦值。原因:*file 類型未實現 DataWriter 接口(丟失 WriteData 方法)。

WriteDataX 方法的簽名本身是合法的。但編譯器掃描到第 33 行代碼時,發現嘗試將 *file 類型賦值給 DataWriter 時,需要檢查 *file 類型是否完全實現了 DataWriter 接口。顯然,編譯器因為沒有找到 DataWriter 需要的 WriteData() 方法而報錯。

2) 實現接口的方法簽名不一致導致的報錯

將修改的代碼恢復后,再嘗試修改 WriteData() 方法,把 data 參數的類型從 interface{} 修改為 int 類型,代碼如下:
func (d *file) WriteData(data int) error {
編譯代碼,報錯:

cannot use f (type *file) as type DataWriter in assignment:
        *file does not implement DataWriter (wrong type for WriteData method)
                have WriteData(int) error
                want WriteData(interface {}) error

這次未實現 DataWriter 的理由變為(錯誤的 WriteData() 方法類型)發現 WriteData(int)error,期望 WriteData(interface{})error。

這種方式的報錯就是由實現者的方法簽名與接口的方法簽名不一致導致的。

接口被實現的條件二:接口中所有方法均被實現

當一個接口中有多個方法時,只有這些方法都被實現了,接口才能被正確編譯并使用。

在本節開頭的代碼中,為 DataWriter中 添加一個方法,代碼如下:
// 定義一個數據寫入器
type DataWriter interface {
    WriteData(data interface{}) error

    // 能否寫入
    CanWrite() bool
}
新增 CanWrite() 方法,返回 bool。此時再次編譯代碼,報錯:

cannot use f (type *file) as type DataWriter in assignment:
        *file does not implement DataWriter (missing CanWrite method)

需要在 file 中實現 CanWrite() 方法才能正常使用 DataWriter()。

Go 語言的接口實現是隱式的,無須讓實現接口的類型寫出實現了哪些接口。這個設計被稱為非侵入式設計。

實現者在編寫方法時,無法預測未來哪些方法會變為接口。一旦某個接口創建出來,要求舊的代碼來實現這個接口時,就需要修改舊的代碼的派生部分,這一般會造成雪崩式的重新編譯。

提示

傳統的派生式接口及類關系構建的模式,讓類型間擁有強耦合的父子關系。這種關系一般會以“類派生圖”的方式進行。經常可以看到大型軟件極為復雜的派生樹。隨著系統的功能不斷增加,這棵“派生樹”會變得越來越復雜。

對于 Go 語言來說,非侵入式設計讓實現者的所有類型均是平行的、組合的。如何組合則留到使用者編譯時再確認。因此,使用 GO 語言時,不需要同時也不可能有“類派生圖”,開發者唯一需要關注的就是“我需要什么?”,以及“我能實現什么?”。

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

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

底部Logo