Go 还是需要泛型的
Go 語言之父早期提到過?less is more[1]?的哲學,可惜社區里有不少人被帶偏了。
每次 Go 增加語法上的新特性、新功能,就會有原教旨主義者跳出來扔出一句 less is more(或者也可能是大道至簡),揚長而去,留下風中凌亂的你。
即使到了官方已經確定要增加泛型功能的 2020 年,依然有人煞有介事地寫文章說為什么?go doesn't need generics[2],作為理智的 Gopher,最好不要對別人的結論盡信,至少要看看其它語言社區怎么看待這件事情。
Java 社區是怎么理解泛型的必要性的呢?
簡而言之,泛型使類型(類和接口)能夠在定義類、接口和方法時成為參數。就像我們更熟悉的在方法聲明中使用的形式參數一樣,類型參數為你提供了一種用不同的輸入重復使用相同代碼的方法。不同的是,形式參數的輸入是值,而類型參數的輸入是類型。
與非泛型代碼相比,使用泛型的代碼有很多好處。
在編譯時進行強類型檢查。Java 編譯器對泛型代碼進行強類型檢查,如果代碼違反類型安全就會報錯。編譯時的錯誤比運行時的錯誤更易修復。
消除類型轉換。下面這段代碼片段在沒有泛型時,需要類型轉換:
用泛型重寫,代碼不再需要進行類型轉換:
List<String>?list?=?new?ArrayList<String>(); list.add("hello"); String?s?=?list.get(0);???//?no?cast程序員可以編寫泛型算法 使用泛型可以實現在不同類型上都可以工作的泛型算法的同時,保證類型安全性。
Gopher 可能對“類型安全”不太熟悉,舉個例子,我們可以用 gods 庫來實現下面的數據結構。
package?mainimport?("fmt"sll?"github.com/emirpasic/gods/lists/singlylinkedlist" )func?main()?{list?:=?sll.New()list.Add("a")?????????????????????//?["a"] }我們的本意是實現一個 string 的單鏈表,但是通用的數據結構庫沒有辦法阻止用戶向該 list 內插入非 string 類型的值,比如用戶可以這樣:
?list?:=?sll.New()list.Add("a")?????????????????????//?["a"]list.Add(2)???????????????????????//?["a",?2]這顯然不是我們想要的結果。
可見泛型最常見的場景是在類型安全的前提下實現算法流程,對于 Go 來說,我們使用的數據結構和算法來源有兩個地方:container 標準庫、第三方數據結構庫,如?gods[3]?。
和我們前面舉的例子一樣,標準庫的通用 container 的大多接口也是接收空 interface{},或返回空 interface{}:
package?mainimport?("container/list" )func?main()?{l?:=?list.New()l.PushBack(4)l.PushFront("bad?value") }做不到類型安全的話,那么用戶代碼就可能在運行期間發生斷言產生的 panic/error。除了容器的功能容易被破壞,類似下面的 bug 也挺容易出現的:
package?mainimport?"fmt"type?mystring?stringfunc?main()?{var?a?interface{}?=?"abc"var?b?interface{}?=?mystring("abc")fmt.Println(a?==?b) }社區的其它嘗試
社區曾經有一些靠代碼生成實現的泛型庫,如genny[4],其本質是使用文本替換來實現多種類型的代碼生成。
genny 使用也比較簡單,比如 example 里的例子:
package?queueimport?"github.com/cheekybits/genny/generic"//?NOTE:?this?is?how?easy?it?is?to?define?a?generic?type type?Something?generic.Type//?SomethingQueue?is?a?queue?of?Somethings. type?SomethingQueue?struct?{items?[]Something }func?NewSomethingQueue()?*SomethingQueue?{return?&SomethingQueue{items:?make([]Something,?0)} } func?(q?*SomethingQueue)?Push(item?Something)?{q.items?=?append(q.items,?item) } func?(q?*SomethingQueue)?Pop()?Something?{item?:=?q.items[0]q.items?=?q.items[1:]return?item }cat source.go | genny gen "Something=string"
//?This?file?was?automatically?generated?by?genny. //?Any?changes?will?be?lost?if?this?file?is?regenerated. //?see?https://github.com/cheekybits/gennypackage?queue//?StringQueue?is?a?queue?of?Strings. type?StringQueue?struct?{items?[]string }func?NewStringQueue()?*StringQueue?{return?&StringQueue{items:?make([]string,?0)} } func?(q?*StringQueue)?Push(item?string)?{q.items?=?append(q.items,?item) } func?(q?*StringQueue)?Pop()?string?{item?:=?q.items[0]q.items?=?q.items[1:]return?item }想實現多種類型的結構就在生成代碼時傳入多種類型就可以了。
這種做法和人們調侃 Go 泛型時使用的?gif[5]?本質上也沒什么區別。
語言的原生支持能讓我們省事,并且也能在實現上更加嚴謹。
在 《Rise and Fall of Software Recipes》一書中,有這么一個故事:
Among the recent projects failing because (or despite) of strong processes, Obamacare is a telling example. It involves 50 contractors, has cost fortunes, was delivered late and crippled with bugs. It was developed using a typical waterfall process, and if only because of that, the Agile community started howling, claiming that they would have made the project a success[Healthcare.gov failure].
And when an Agile project fails like Universal Credit in Great-Britain[UniversalCredit] [NAO2013], even when the full report states that the lack of detailed blueprint – typical of Agile methodologies – was one of the factors that caused the failure, common Agile wisdom says it is because it was not applied properly, or should I say, not Agile enough.
簡而言之,就是敏捷大師們其實非常雙標,他們給出的方法論也不一定靠譜,反正成功了就是大師方法得當,失敗了就是我們執行不力沒有學到精髓。正著說反著說都有道理。
再看看現在的 Go 社區,buzzwords 也很多,如果一個特性大師不想做,那就是 less is more。如果一個特性大師想做,那就是 orthogonal,非??陀^。
對于不想迷信大師的 Gopher 來說,多聽聽批評意見沒壞處:go is not good[6]。
[1]
less is more:?https://en.wikipedia.org/wiki/Less_is_more
[2]go doesn't need generics:?https://dzone.com/articles/go-doesnt-need-generics
[3]gods:?https://github.com/emirpasic/gods
[4]genny:?https://github.com/cheekybits/genny
[5]gif:?https://twitter.com/yogthos/status/883058510275149826
[6]go is not good:?https://github.com/ksimka/go-is-not-good
總結
以上是生活随笔為你收集整理的Go 还是需要泛型的的全部內容,希望文章能夠幫你解決所遇到的問題。