golang没有泛型
golang是一门简单易用的语言,没有泛型,导致有些代码写起来比较啰嗦,像一些序列化反序列化方面的需求,对不同类型的操作都要分别实现,代码冗余度较高.
比如说这样一个需求:
数据结构对象的存储和读取
1
2
3
4
5
6
7
8
|
type A struct {
t int `json:"tt"`
}
type B struct {
tt int `json:"tt"`
xx int `json:"xx"`
}
|
redis里分别存入了A
和B
的若干对象的json序列化字符串,现在要取出所有的A
类型对象和所有的B
类型对象
取出A
类型对象的方法
1
|
GetA() (output []A, err error)
|
取出B
类型对象的方法
1
|
GetB() (output []B, err error)
|
相似的逻辑要分别写两遍.
golang反射
那么针对这样的需求,只能一遍又一遍的去实现逻辑近似的函数吗?可以看看一些标准的序列化反序列化库是怎么做的,典型的像json序列化反序列化.
序列化接口:
1
|
func Marshal(v interface{}) ([]byte, error)
|
反序列化接口:
1
|
func Unmarshal(data []byte, v interface{}) error
|
可以看到这里使用interface{}
这个类型作为参数,代表任意类型,这就是golang解决这类问题的方法: interface{}
配合golang的反射.
回头来看上面的需求,把[]A
和[]B
使用interface{}
替代,不要使用interface{}
作为返回值,否则使用接口的时候又要做一次类型转换,复杂度仍然是带到了使用方. 参考json反序列化的接口,将返回值作为一个inout
参数.
先来看一个初始版本
1
2
3
4
5
6
7
8
9
10
|
func Get(out interface{}, outhint interface{}) {
v := reflect.ValueOf(out).Elem()
v.Set(reflect.MakeSlice(v.Type(), 2, 2))
for i, w := range []string{"{\"tt\":1}", "{\"tt\":2}"} {
json.Unmarshal([]byte(w), outhint)
fmt.Println("out: ", outhint)
v.Index(i).Set(reflect.ValueOf(outhint).Elem())
}
fmt.Println(v)
}
|
这个版本比较简单,其中out
参数表示结果slice,outhint
参数表示slice内的元素类型,这个参数降低了代码复杂度.
可以看到函数实现中的直接使用outhint
作为jsonUnmarshal()
,reflectValueOf()
的参数.
实际使用时outhint
就是out
这个slice内的元素的类型, outhint
和out
显然是有关联的,outhint
这个参数是可以省略的.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
func GetSlice(out interface{}) {
v := reflect.ValueOf(out).Elem()
v.Set(reflect.MakeSlice(v.Type(), 2, 2))
vtype := reflect.TypeOf(out).Elem()
vele := vtype.Elem()
fmt.Println("vele type: ", vele.Name())
if vele.Kind() == reflect.Ptr {
fmt.Println("element is pointer type")
//模拟从redis里遍历符合条件的元素
for i, w := range []string{"{\"tt\":1}", "{\"tt\":2}"} {
elev := reflect.New(vele.Elem())
tmp := elev.Interface()
json.Unmarshal([]byte(w), tmp)
// fmt.Println("76 out: ", tmp)
v.Index(i).Set(elev)
}
} else {
fmt.Println("element is struct type")
for i, w := range []string{"{\"tt\":1}", "{\"tt\":2}"} {
elev := reflect.New(vele)
tmp := elev.Interface()
json.Unmarshal([]byte(w), tmp)
// fmt.Println("76 out: ", tmp)
v.Index(i).Set(elev.Elem())
}
}
}
|
上面这个函数使用interface{}
和反射实现了泛型需求.可以看到,其中类型判断还是不太安全的,写法也比较复杂
golang1.18将会加入泛型,到时候对于这类需求将会有更好的实现方式.