介面

介面 在 Go

2 個練習

關於 介面

介面是一組方法

在最簡單的形式裡,介面型別是一組方法簽章。 以下是一個介面定義的範例,其中包含 Add 和 Value 這兩個方法:

type Counter interface {
    Add(increment int)
    Value() int
}

像 increment 這樣的參數名稱可以從介面定義中省略,不過它們通常能提升可讀性。

Go 的介面名稱不包含 Interface 或 I 這個字。 它們通常以 er 結尾,例如 Reader、Stringer。

實作介面

任何定義了介面方法的型別,都會自動隱含地「實作」該介面。 Go 裡沒有 implements 關鍵字。

以下型別實作了我們上面看到的 Counter 介面。

type Stats struct {
    value int
    // ...
}

func (s Stats) Add(v int) {
    s.value += v
}

func (s Stats) Value() int {
    return s.value
}

func (s Stats) SomeOtherMethod() {
    // The type can have additional methods not mentioned in the interface.
}

就實作介面而言,方法擁有的是值接收者還是指標接收者並不重要。 (如果你對這些還不熟,可以回頭看看方法的概念。)

介面型別的值可以保存任何有實作這些方法的值。1

這表示 Stats 現在可以用在所有需要 Counter 介面的地方。

func SetUpAnalytics(counter Counter) {
    // ...
}

stats := Stats{}
SetUpAnalytics(stats)
// works because Stats implements Counter

如果函式的所有參數和回傳值都必須使用具體(也就是非介面)型別,會嚴重限制它們的重用性。 只要讓參數改用介面型別,就只會定義函式完成工作所需的行為。 這麼一來,函式就能搭配不同具體型別的參數使用,只要那些具體型別有實作這個介面就好。 同樣的邏輯也適用於以介面型別作為回傳值的情況。

因為介面是隱含實作的,所以一個型別可以輕鬆實作多個介面。 它只需要定義好所有必要的方法。

組合介面

介面定義可以包含其他介面。 以下是標準函式庫裡的一個範例。

type ReadCloser interface {
	Reader
	Closer
}

// Based on these interfaces:

type Reader interface {
	Read(p []byte) (n int, err error)
}

type Closer interface {
	Close() error
}

定義 ReadCloser 時,介面定義沒有重複寫出 Read 和 Close 的方法簽章,而是沿用了 Reader 和 Closer 現有的定義。

空介面

Go 裡有一個非常特別的介面型別,也就是不含任何方法的空介面。 空介面寫成這樣:interface{}。 在 Go 1.18 或更高的版本中,也可以使用 any。 它被定義為別名。

由於空介面沒有任何方法,因此每個型別都隱含地實作了它。 這在定義能通用地接受任何值的函式時很有幫助。 在這種情況下,函式參數會使用空介面型別。

例如:

func AnythingGoes(i interface{}) string {
    if i == 0 {
        return "zero"
    }
    if i == "" {
        return "empty string"
    }
    return "something else"
}
  1. Go 導覽:介面 ↩

透過 GitHub 編輯 連結會在新視窗或分頁中開啟

學習 介面

練習已鎖定

再解鎖 1 個練習,就能練習 介面