Golang 反射和接口类型判断

一、空接口类型判断

一个空接口可能被赋予不同结构体值的场景时,通过反射机制TypeOf可以知道空接口的动态类型但是不能直接创建动态类型的对象,必须通过switch case 或者if else 的形式显示声明。空接口(interface {})类型判断:

1.1.直接断言

var param interface{} ; val, ok := param.(string)
如果返回ok为true,则变量param为string类型,同时返回一个val存储string类型的值。
var param interface{} ; val := param.(string)
如果我们确定param为string类型,也可以不返回ok变量,直接强转获取其值。

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解析库慢,一部分原因就是因为其中涉及到了反射,所以如果对效率有要求的地方就要斟酌使用了。
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值