一、空接口类型判断
一个空接口可能被赋予不同结构体值的场景时,通过反射机制TypeOf可以知道空接口的动态类型但是不能直接创建动态类型的对象,必须通过switch case 或者if else 的形式显示声明。空接口(interface {})类型判断:
1.1.直接断言:
var param interface{} ; val, ok := param.(string)var param interface{} ; val := param.(string)
1.2.反射判断:
反射位于relfect包,可以得知空接口的具体类型名字,但是空接口不能通过反射机制直接创建空接口的动态类型对象。获取类型使用reflect.TypeOf,获取值使用reflect.ValueOf,具体使用方法:
var param interface{}
retType = reflect.TypeOf(param)
val = reflect.ValueOf(param)
要通过反射创建空接口的动态类型对象,多和switch一起使用
type Student struct {
name string
age int
}
//switch val:=reflect.ValueOf(x).Type().String()
switch reflect.TypeOf(x).String() {
case "Int":
case "model.Student":
````````````````````````
case "string":
`````````````````````````````
}
reflect包使用Kind类型来表示类型所属的分类,它描述的是 Type 被归属于哪一类,在 Golang 里,所有 Type 都能归属到下面这些类别。但是对于复合类型只能用kind判断所属的大类别,不能得知具体的所属类型变量(struct、slice、map等)。
```go
const (
Invalid Kind = iota;Bool; Int; Int8; Int16 ;Int32;Int64;Uint
Array;Chan;Func; Interface;Map;Ptr; Slice;String;Struct;
UnsafePointer;.......
)
用reflect.ValueOf(x).Kind()的用例:
v := reflect.ValueOf(x)
k := v.Kind()
switch k {
case reflect.Int64:
// v.Int()从反射中获取整型的原始值,然后通过int64()强制类型转换
fmt.Printf("type is int64, value is %d\n", int64(v.Int()))
case reflect.Struct:
// v.Float()从反射中获取浮点型的原始值,然后通过float32()强制类型转换
fmt.Printf("type is float32, value is %f\n", float32(v.Float()))
case reflect.Ptr:
// v.Float()从反射中获取浮点型的原始值,然后通过float64()强制类型转换
fmt.Printf("type is float64, value is %f\n", float64(v.Float()))
}
1.4 接口类型分支断言 val.(type):
val.(type)返回值的数据类型,能得到具体的哪一类结构体,多和switch 一起使用。用于判断一个接口值的实际类型是否为某个类型,或一个非接口值的类型是否实现了某个接口类型。
//用于判断一个接口值的实际类型是否为某个类型
type Student struct {
name string
age int
}
switch value := data.(type) {
case int:
fmt.Printf("x[%d] 类型为int,内容为%d\n",index,value)
case string:
fmt.Printf("x[%d] 类型为string,内容为%s\n",index,value)
case Student: //结构体
fmt.Printf("x[%d] 类型为Student,内容为name=%s,age=%d\n",index,value.name,value.age)
}
}
//一个非接口值的类型是否实现了某个接口类型
func Accessor(obj interface{}) (metav1.Object, error) {
// 使用了golang的switch type语法
switch t := obj.(type) {
// 因为API对象类型都继承了metav1.ObjectMeta,也就自然实现了metav1.Object。
case metav1.Object:
return t, nil
// metav1.ObjectMeta也实现了metav1.ObjectMetaAccessor
case metav1.ObjectMetaAccessor:
if m := t.GetObjectMeta(); m != nil {
return m, nil
}
return nil, errNotObject
default:
return nil, errNotObject
}
}
1.5 Unmarshal与interface{}:
type httpResp struct {
RetCode int `json:"ret_code"`
Data interface{} `json:"data"`
Message string `json:"message"`
}
response,err := HTTPPost(completeURL, data, timeout)
json.Unmarshal([]byte(response), &httpRsp)
- 如上述代码所示,若response是一个string的json嵌套串,如下。
{"ret_code":0,"data":{"name":"tars.tarsadmin.center","desired":2,"updated":2,
"total":2,"available":2,"unavailable":0,"age":245176,"images":"tars.dockerhub.com:5000/app/tarsadmin:1.1.0",
"pods":[{"name":"tars.tarsadmin.center-5b87d44f9d-4cg6g","containerNum":1,"containerReady":1,"status":"Running","restarts":0,"age":245176,"ip":"0.0.0.0","node":"dev1-3","nodeIP":"0.0.0.0"},
{"name":"tars.tarsadmin.center-5b87d44f9d-6rmmh","containerNum":1,"containerReady":1,"status":"Running",
"restarts":0,"age":245176,"ip":"0.0.0.0","node":"dev1-1","nodeIP":"0.0.0.0"}]},"err_msg":""}
则response的data字段经过unMarshal后会变成map[string]interface{}的形式。
-
func Unmarshal(data []byte, v interface{}) error {—}
data入参要是string或[]byte的形式,将data JSON字符串解码到v的数据结构中,v可为map或者结构体struct。若v的数据结构不显示声明为一个具体的结构体或者map,那么在反解析后v的数据结构类型为map[string]interface{}。 -
var request interface{}
err := json.Unmarshal([]byte(req), &request)
request为map[string]interface{}
二、反射和接口
所谓的静态类型(即 static type),就是变量声明的时候的类型。接口的静态类型是接口本身。
var age int // int 是静态类型
var name string // string 也是静态类型
所谓的 动态类型(即 concrete type,也叫具体类型)是 程序运行时系统才能看见的类型。一个变量的动态类型在运行时可能改变,这主要依赖于它的赋值(前提是这个变量是接口类型)。
非空接口:
var reader io.Reader
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
return nil, err
}
reader = tty
空接口:
//不带函数的interface
var empty interface{}
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
return nil, err
}
empty = tty
反射:reflect 反射包可以直接获取到接口变量当前的数据类型(动态类型)、当前的值,不需要再进行判断。reflect.TypeOf ()接口变量当前的数据类型。reflect.valueof()接口变量当前的值。由于动态类型的存在,在一个函数中接收的参数的类型有可能无法预先知晓,此时我们就要对参数进行反射,然后根据不同的类型做不同的处理。例如reflect.TypeOf 函数的唯一参数的类型为 interface{},reflect.TypeOf 函数接受任何的interface{}参数,并且把接口中的动态类型以reflect.type形式返回。reflect.ValueOf 函数接受任何的interface{}参数,并且把接口中的动态值以reflect.value形式返回。反射类型获取的源码如下:
// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
typ *rtype
word unsafe.Pointer
}
// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i interface{}) Type {
eface := *(*emptyInterface)(unsafe.Pointer(&i))
return toType(eface.typ)
}
//type中包含了类型信息。
func toType(t *rtype) Type {
if t == nil {
return nil
}
return t
}
示例: 调用函数入参object是一个接口类型
func configInstanceDetail(object runtime.Object) {
configWorkloadImage(spec, repository)
template, err := json.Marshal(object)
...
}
//json.marshal源码,marshal传入的是空接口。
func Marshal(v interface{}) ([]byte, error) {
e := newEncodeState()
err := e.marshal(v, encOpts{escapeHTML: true})
if err != nil {
return nil, err
}
buf := append([]byte(nil), e.Bytes()...)
e.Reset()
encodeStatePool.Put(e)
return buf, nil
}
//通过反射获取接口的动态值。
func (e *encodeState) marshal(v interface{}, opts encOpts) (err error) {
defer func() {
if r := recover(); r != nil {
if je, ok := r.(jsonError); ok {
err = je.error
} else {
panic(r)
}
}
}()
e.reflectValue(reflect.ValueOf(v), opts)
return nil
}
代码调用链:json.Marshal -> e.marshal -> e.reflectValue -> valueEncoder -> typeEncoder -> newTypeEncoder -> newStructEncoder -> se.encode。其中 newTypeEncoder如下:
// newTypeEncoder constructs an encoderFunc for a type.
// The returned encoder only checks CanAddr when allowAddr is true.
func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
...........
switch t.Kind() {
case reflect.Bool:
return boolEncoder
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return intEncoder
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return uintEncoder
case reflect.Float32:
return float32Encoder
case reflect.Float64:
return float64Encoder
case reflect.String:
return stringEncoder
case reflect.Interface:
return interfaceEncoder
case reflect.Struct:
return newStructEncoder(t)
case reflect.Map:
return newMapEncoder(t)
case reflect.Slice:
return newSliceEncoder(t)
case reflect.Array:
return newArrayEncoder(t)
case reflect.Ptr:
return newPtrEncoder(t)
default:
return unsupportedTypeEncoder
}
}
反射也存在两个必然的问题:
- 第一个是不安全,因为反射的类型在转换中极易出现问题,所以使用需谨慎。
- 第二个是速度慢,之所以有人抨击golang的json解析库慢,一部分原因就是因为其中涉及到了反射,所以如果对效率有要求的地方就要斟酌使用了。