如果某个函数的入参的类型是 interface{}
,有下面几种方法可以获取入参的类型:
获取入参类型的方法
1 反射
import (
"reflect"
"fmt"
)
func main() {
v := "hello world"
fmt.Println(typeof(v))
}
func typeof(v interface{}) string {
return reflect.TypeOf(v).String()
}
Example
其实,fmt.Sprintf("%T")
底层也是用反射实现的:
func (p *pp) printArg(arg interface{}, verb rune) {
...
// Special processing considerations.
// %T (the value's type) and %p (its address) are special; we always do them first.
switch verb {
case 'T':
p.fmt.fmtS(reflect.TypeOf(arg).String())
return
...
}
}
Usage:
import "fmt"
func main() {
v := "hello world"
fmt.Println(typeof(v))
}
func typeof(v interface{}) string {
return fmt.Sprintf("%T", v)
}
2 类型断言(Type Assertion)
安全的类型断言(Type Assertion)
func main() {
v := "hello world"
fmt.Println(typeof(v))
}
func typeof(v interface{}) string {
switch t := v.(type) {
case int:
return "int"
case float64:
return "float64"
//... etc
default:
_ = t
return "unknown"
}
}
注意,这里的 v 必须为 initerface{}
类型才可以进行类型断言。比如下面代码就会报错:
s := "BrainWu"
if v, ok := s.(string); ok { // invalid type assertion: s.(string) (non-interface type string on left)
fmt.Println(v)
}
在这里只要是在声明时或函数传进来的参数不是 interface{}
类型,那么做类型断言都是会报 non-interface
错误的。
所以我们只能通过将 s 先转换为一个 interface{}
类型,然后才能进行类型断言:
s := "BrainWu"
if v, ok := interface{}(s).(string); ok {
fmt.Println(v)
}
分析
reflect.TypeOf()
的参数是 i interface{}
,返回一个Type
类型的struct:
// 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)
}
// rtype is the common implementation of most values.
// It is embedded in other struct types.
//
// rtype must be kept in sync with ../runtime/type.go:/^type._type.
type rtype struct {
size uintptr
ptrdata uintptr // number of bytes in the type that can contain pointers
hash uint32 // hash of type; avoids computation in hash tables
tflag tflag // extra type information flags
align uint8 // alignment of variable with this type
fieldAlign uint8 // alignment of struct field with this type
kind uint8 // enumeration for C
alg *typeAlg // algorithm table
gcdata *byte // garbage collection data
str nameOff // string form
ptrToThis typeOff // type for pointer to this type, may be zero
}
Main:
package main
import (
"fmt"
"reflect"
)
type AAA struct {
bb string
}
func main() {
a := AAA{bb: "temp"}
fmt.Printf("%v", reflect.TypeOf(a))
}
我们来看看 Golang 的反射是怎么做到的?
在 Golang中,interface也是一个结构体,包含两个attribute(分别是 1 个指针):
- 指针1:指向该变量的类型
- 指针2:指向该变量的value
如下,空 interface (即未定义任何内容的interface)对应的结构体如下所示:
// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
typ *rtype
word unsafe.Pointer
}
第一个指针的类型是type rtype struct
。
非空 interface 由于需要携带的信息更多(例如该接口实现了哪些方法),所以第一个 attribute 是 itab,它是一个 struct 指针类型,在这个 struct 中记录了该变量的动态类型 typ *rtype
。
// nonEmptyInterface is the header for a interface value with methods.
type nonEmptyInterface struct {
// see ../runtime/iface.go:/Itab
itab *struct {
ityp *rtype // static interface type
typ *rtype // dynamic concrete type
link unsafe.Pointer
bad int32
unused int32
fun [100000]unsafe.Pointer // method table
}
word unsafe.Pointer
}
我们来看看 reflect.TypeOf()
:
// 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)
}
可以看到,调用 TypeOf
时传入的是 interface{}
,它将变量的地址转换为空接口,然后将得到的 rtype
实例转为 Type
接口返回。
需要注意,当调用 reflect.TypeOf
的之前,其实已经发生了一次隐式的类型转换,即将具体类型的向空接口转换。这个过程比较简单,只要拷贝typ *rtype
和word unsafe.Pointer
就可以了。
例如w := os.Stdout
,该变量的接口值在内存里是这样的:
那么,类型断言是怎么判断是不是某个接口呢?回到最初,在golang中,接口是一个松耦合的概念,一个类型是不是实现了某个接口,就是看该类型是否实现了该接口要求的所有函数,所以,类型断言判断的方法就是检查该类型是否实现了接口要求的所有函数。
Reference
- https://ieevee.com/tech/2017/07/29/go-type.html
- https://blog.csdn.net/u012807459/article/details/30050391
- https://medium.com/golangspec/type-assertions-in-go-e609759c42e1