用 Go interface{} 等於什麼也沒說

大家好,我是煎魚。

如果說在 Go 裏要有一句與 interface{} 相關,你會想到什麼?是萬物皆要定義 interface,否則沒法抽象?

Go 諺語中認可的是:"interface{} says nothing",也就是 interface{} 什麼也沒說。這指的又什麼,太黑話了吧...

今天就煎魚和大家一起學習。

接口類型無自描述

interface{} 的第一種用法,那就是變量的數據類型聲明。結合其它語言來看,一共有如下幾種形式:

let i:any = 1;          // Typescript
std::any i = 1;         // C++17
Object i = 1;           // Java
var i interface{} = 1   // Go

Go 在 1.18 後,也支持了 any 關鍵字的聲明方法,是類似 interface{} 的作用,各路語言都趨同了。

在實際編程中頻繁的用接口(interface{})類型作爲變量的類型有沒有問題呢?

明確的聲明

當我們在閱讀 Go 代碼時。如果文檔、命名、、參數(含類型)是清晰的,可靠的。我們大概率會直接調用,明確的類型會更讓我們有 ” 安全感 “,知道要傳什麼值。

如下函數簽名:

func Eat(v string) { ... }

當然知道調用 Eat 函數要傳 string 類型了,不是傳什麼 int 類型。

未知的聲明

如果一個函數的參數的類型是 interface{},我們就會進函數內看其具體的實現,以此尋求確定性。

如下函數簽名:

func Eat(v interface{}) { ... }

請問變量 v 到底傳什麼,傳 int 類型,還是 string 類型,又或是都可以?

正如諺語中所說,定義了 interface{},是什麼都沒說,顯然是 “不大好的味道”,這樣的代碼無法自描述。程序員得翻代碼或文檔(文檔還不一定更新的及時)。

注:在公司真見到這種場景,該位同學猜不透,大呼絕絕子,翻代碼去了。

小接口優於大接口

在 Go 的標準庫中,package io 的 io.Reader 和 io.Writer 接口是官方認可的教科書式案例,小而美的接口是編寫強大而靈活的 Go 代碼的關鍵。

io.Reader:

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

io.Writer:

type Writer interface {
 Write([]byte) (n int, err error)
}

小接口與大接口相比,用戶認知的心智和實現成本較低。

從現實情況來講,當一個 Go 代碼庫中擁有 6 個,甚至更多的大型接口往往只有兩種實現,那就是唯一的具體實現和一個用於測試的模擬實現。

另外從歷史的角度來看, io.Reader 和 io.Writer 接口並不是前期設計的,它們是後來發現的。Network、File 和其他字節處理類型需要共享類似的實現,才誕生的 io.Reader 和 io.Writer 。

“最佳實踐” 都是實踐、探索、演變出來的。

總結

今天我們對 Go 諺語中的:"interface{} says nothing" 進行了大致的瞭解,內容不多,核心的官方建議在於:

你覺得這個 Go 諺語靠譜嗎?你是否有大接口的使用經驗?

Go1.18 有了泛型後,泛型具有的相對定義,是否可以解決這個問題?

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/3MQaXaPkrxLg04d1vBjVnw