Go语言的类型检查有两种技术,一种是 Type Assert
,一种是Reflection
go的类型检查
Type Assert 转型操作
对某个变量进行 .(type)
的转型操作,
会返回两个值 variable
, error
第一个返回值是被转换好的类型
第二个是如果不能转换类型,则会报错
package main_test
import "testing"
type Container []interface{}
func (c *Container) Put(ele interface{}){
*c = append(*c, ele)
}
func (c *Container) Get() interface{}{
ele := (*c)[0]
*c = (*c)[1:]
return ele
}
func TestPut(t *testing.T){
intContainer := &Container{}
intContainer.Put(8)
intContainer.Put(42)
ele, ok := intContainer.Get().(int)
// 把数据取出来时,因为类型是 interface{}
// 所以,你还要做一个转型,如果转型成功能才能进行后续操作(因为 interface{}太泛了,泛到什么类型都可以放)
if !ok{
t.Log("不是一个整数")
}
t.Log(ele)
}
Reflection 反射
package main_test
import (
"fmt"
"reflect"
"testing"
)
type Container struct{
s reflect.Value
}
// NewContainer()会根据参数的类型初始化一个Slice
func NewContainer(t reflect.Type, size int) *Container {
if size <=0 {size=64}
return &Container{
s: reflect.MakeSlice(reflect.SliceOf(t), 0, size),
}
}
// Put()时候,会检查 val 是否和Slice的类型一致
func (c *Container) Put(val interface{}) error{
if reflect.ValueOf(val).Type() != c.s.Type().Elem(){
return fmt.Errorf("put : cannot put a %T into a slice of %s",
val, c.s.Type().Elem())
}
c.s = reflect.Append(c.s, reflect.ValueOf(val))
return nil
}
// Get()时,我们需要用一个入参的方式,因为我们没有办法返回 reflect.Value 或是 interface{},不然还要做Type Assert
func (c *Container) Get(refval interface{}) error{
if reflect.ValueOf(refval).Kind() != reflect.Ptr ||
reflect.ValueOf(refval).Elem().Type() != c.s.Type().Elem(){
return fmt.Errorf(
"Get: needs *%s but got a %T", c.s.Type().Elem(), refval)
}
reflect.ValueOf(refval).Elem().Set(c.s.Index(0))
c.s = c.s.Slice(1, c.s.Len())
return nil
}
func TestReflection(t *testing.T){
f1 := 3.1415926
f2 := 1.41421356237
c := NewContainer(reflect.TypeOf(f1), 16)
if err := c.Put(f1); err != nil {
panic(err)
}
if err := c.Put(f2); err != nil {
panic(err)
}
g := 12
if err := c.Get(&g); err != nil {
panic(err)
}
fmt.Printf("%v (%T)\n", g, g) //3.1415926 (float64)
fmt.Println(c.s.Index(0)) //1.4142135623
}
Go Generator(模板)
C++
模板的具体化
C++的编译器会在编译时分析代码,
根据不同的变量类型来自动化的生成相关类型的函数或类
//用<class T>来描述泛型
template <class T>
T GetMax (T a, T b) {
T result;
result = (a>b)? a : b;
return (result);
}
int i=5, j=6, k;
//生成int类型的函数
k=GetMax<int>(i,j);
long l=10, m=5, n;
//生成long类型的函数
n=GetMax<long>(l,m);
Go的代码生成
需要三件事:
一个函数模板,其中设置好相应的占位符。
filter.tmp.go
package PACKAGE_NAME
type GENERIC_NAMEList []GENERIC_TYPE
type GENERIC_NAMEToBool func(*GENERIC_TYPE) bool
func (al GENERIC_NAMEList) Filter(f GENERIC_NAMEToBool) GENERIC_NAMEList {
var ret GENERIC_NAMEList
for _, a := range al {
if f(&a) {
ret = append(ret, a)
}
}
return ret
}
一个脚本,用于按规则来替换文本并生成新的代码。
#!/bin/bash
set -e
SRC_FILE=${1}
PACKAGE=${2}
TYPE=${3}
DES=${4}
#uppcase the first char
PREFIX="$(tr '[:lower:]' '[:upper:]' <<< ${TYPE:0:1})${TYPE:1}"
DES_FILE=$(echo ${TYPE}| tr '[:upper:]' '[:lower:]')_${DES}.go
sed 's/PACKAGE_NAME/'"${PACKAGE}"'/g' ${SRC_FILE} | \
sed 's/GENERIC_TYPE/'"${TYPE}"'/g' | \
sed 's/GENERIC_NAME/'"${PREFIX}"'/g' > ${DES_FILE}
//4个参数:
//模板源文件
//包名
//实际需要具体化的类型
//用于构造目标文件名的后缀
一行注释代码。
type Employee struct {
Name string
Age int
Vacation int
Salary int
}
//go:generate ./gen.sh ./template/filter.tmp.go gen Employee filter
func filterEmployeeExample() {
var list = EmployeeList{
{"Hao", 44, 0, 8000},
{"Bob", 34, 10, 5000},
{"Alice", 23, 5, 9000},
{"Jack", 26, 0, 4000},
{"Tom", 48, 9, 7500},
}
var filter EmployeeList
filter = list.Filter(func(e *Employee) bool {
return e.Age > 40
})
fmt.Println("----- Employee.Age > 40 ------")
for _, e := range filter {
fmt.Println(e)
}
filter = list.Filter(func(e *Employee) bool {
return e.Salary <= 5000
})
fmt.Println("----- Employee.Salary <= 5000 ------")
for _, e := range filter {
fmt.Println(e)
}
}
酷 壳 – COOLSHELL – Go Generator