這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
原文:Go – taking slices of any type as input parameters
譯者:youngsterxyf
最近參與的一個業餘項目,go-linq,讓我瞭解到Go語言的類型系統並不是為任何類物件導向編程而設計的。沒有泛型,沒有類型繼承,也沒有提供任何對這些特性有用的東西。
但是,提供了一個名為interface{}
的類型,你可以向其賦予幾乎任意類型的值,不會拋出編譯錯誤,就像.NET的Object
或Java的Object
:
var o interface{}o := 3.14o := Student{Name: "Ahmet"}
我們假設你需要一個可以接收任意類型slices的函數,如果考慮如下這樣實現:
func Method(in []interface{}){...}...slice := []int{1, 2, 3}Method(slice) // 拋出錯誤
這樣的代碼會拋出編譯錯誤,因為[]int
不是[]interface{}
。那麼該如何解決這個問題呢?你可以要求Method
的使用者先把slices轉換為[]interface{}
類型。也就是說他們必須藉助於如下類似函數將他們的[]AnyType
型別參數轉換為[]interface{}
類型:
func conv(in []AnyType) (out []interface{}) { out = make([]interface{}, len(in)) for i, v := range in { out[i] = v } return}
但這種實現的擴充性並不好。如果Method
的使用者(可以是一個常用函數如Map
、Filter
等)想向Method
傳遞N種不同類型的參數,那麼他們就必須編寫N個conv
函數。
對此,我們該怎麼辦呢?使用reflection(反射)呀!實現一個函數以interface{}
(可以賦任意類型的值)為輸入參數類型,在函數內部將這個輸入參數轉換為一個slice,然後用於我們Method
函數。如下所示:
func takeSliceArg(arg interface{}) (out []interface{}, ok bool) { slice, success := takeArg(arg, reflect.Slice) if !success { ok = false return } c := slice.Len() out = make([]interface{}, c) for i := 0; i < c; i++ { out[i] = slice.Index(i).Interface() } return out, true}func takeArg(arg interface{}, kind reflect.Kind) (val reflect.Value, ok bool) { val = reflect.ValueOf(arg) if val.Kind() == kind { ok = true } return}
函數takeArg()
嘗試將傳入的參數值轉換為指定的reflect.Kind類型,然後函數takeSliceArg()
嘗試將傳遞給它的值(經takeArg()
轉換後)轉換為一個interface{}
的slice。雖然,這樣會因為反射而影響到一點效能,但影響並不大。
就是這樣了。這種方案啟發於Tobia Confronto的fn項目,並應用到go-linq中。