接口详解
// 举例:sort包中的 Sort 函数,如下: func Sort(data Interface) Sort sorts data. It makes one call to data.Len to determine n, and O(n*log(n)) calls to data.Less and data.Swap. The sort is not guaranteed to be stable. (Sort 对 data 进行排序。 它调用一次 data.Len 来决定排序的长度 n,调用 data.Less 和 data.Swap 的开销为 O(n*log(n))。此排序为不稳定排序。) type Interface interface { // Len is the number of elements in the collection. Len() int // Less reports whether the element with // index i should sort before the element with index j. Less(i, j int) bool // Swap swaps the elements with indexes i and j. Swap(i, j int) }
举例来扩展 Sort 函数的功能,代码如下:
package main import ( "fmt" "sort" "math/rand" ) type Student struct { Name string Id string Age int } type StudentSlice []Student // 自定义一个切片类型 StudentSlice,切片中的元素是 Student 类型 func (p StudentSlice) Len() int { return len(p) } func (p StudentSlice) Less(i, j int) bool { return p[i].Name < p[j].Name } func (p StudentSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } // StudentSlice 类型完全实现了 sort.Sort() 形参中的 Interface 类型,那么 就可以用 sort.Sort() 函数对 StudentSlice 类型的数据进行排序 func main(){ var stuslice StudentSlice for i := 0; i < 10; i++ { stu := Student{ // 生成 Student 类型的结构体 Name: fmt.Sprintf("stu%d",rand.Intn(100)), Id:fmt.Sprintf("110%d",rand.Int()), Age: rand.Intn(100), } stuslice = append(stuslice,stu) // Student 类型的结构体添加到 stuslice 切片中 } for _, v := range stuslice { fmt.Println(v) } fmt.Println(" ") sort.Sort(stuslice) // 由于stuslice 类型实现了Interface这个接口,那么就能调用 sort.Sort() 对其进行排序 for _, v := range stuslice { fmt.Println(v) } } // 编译后运行结果如下: [root@NEO project]# go build -o bin/example01_interface_extend go_dev/day06/example01_interface_extend/main [root@NEO project]# bin/example01_interface_extend {stu81 1108674665223082153551 47} {stu59 1103916589616287113937 18} {stu25 1101443635317331776148 56} {stu0 1104751997750760398084 11} {stu62 1103510942875414458836 28} {stu74 1102610529275472644968 45} {stu37 1102015796113853353331 95} {stu66 1105263531936693774911 58} {stu47 1102740103009342231109 87} {stu88 1107981306761429961588 15} {stu0 1104751997750760398084 11} {stu25 1101443635317331776148 56} {stu37 1102015796113853353331 95} {stu47 1102740103009342231109 87} {stu59 1103916589616287113937 18} {stu62 1103510942875414458836 28} {stu66 1105263531936693774911 58} {stu74 1102610529275472644968 45} {stu81 1108674665223082153551 47} {stu88 1107981306761429961588 15} [root@NEO project]# // 任何类型,只要实现了它的规范(接口),就可以调用它的函数来排序
断言
// 示例代码: package main import ( "fmt" ) func main(){ var a interface{} // 定义一个空接口 var b int a = b c := a.(int) // 断言;把 接口a 转成 int 型 fmt.Printf("%d %T ",c,c) d,ok := a.(string) // 断言(加判断);检查是否能转成 string 类型,并带判断 if ok == false { fmt.Println("convert failed") return } fmt.Println("%d :string type",d) } // 运行结果: [root@NEO example01_interface_assert]# go run main/main.go 0 int convert failed [root@NEO example01_interface_assert]#
类型断言:(采用type switch方式)
写一个函数判断传入参数的类型
// 示例代码: package main import "fmt" func classifier(items...interface{}){ for i,x := range(items){ switch x.(type){ // 接口.(type) 要和 switch 一起用 case bool: fmt.Printf("param #%d is a bool,val is %v ", i,x) case float64: fmt.Printf("param #%d is a float64,val is %v ", i,x) case int,int64: fmt.Printf("param #%d is a int,val is %v ",i,x) case nil: fmt.Printf("param #%d is nil,val is %v ", i,x) case string: fmt.Printf("param #%d is a string,val is %v ", i,x) default: fmt.Printf("param #%d’s type is unknown,val is %v ", i,x) } } } func main() { var a int classifier(a,"abc",3.3) } // 运行结果: [root@NEO example01_interface_assert02]# go run main/main.go param #0 is a int,val is 0 param #1 is a string,val is abc param #2 is a float64,val is 3.3 [root@NEO example01_interface_assert02]#
判断一个变量是否实现了指定接口
// 示例代码: package main import "fmt" type Stringer interface { Strings() string } type MyStruct struct{ Name string Id int } func (p *MyStruct) Strings() string{ return p.Name } func main() { var vp *MyStruct = &MyStruct{ Name:"mystruct", Id: 110, } var t interface{} t = vp // 要判断变量是否实现了某个接口,必须要选择该变量赋值给一个接口再调用下面的方法 if v,ok := t.(Stringer);ok { // 此处只能 t.(Stringer),即只能用接口去调用 .(接口名) (把变量赋值给接口再判断);返回两个值:接口t指向的变量 和 判断结果(true 或 false) fmt.Printf("vp implements Strings():%s; v:%v; ok:%v ",v.Strings(),v,ok) } } // 运行结果: [root@NEO example01_interface_var2interface]# go run main/main.go vp implements Strings():mystruct; v:&{mystruct 110}; ok:true [root@NEO example01_interface_var2interface]#
实现一个通用的链表类
// 目录结构如下: example01_interface_linkedlist ├── linkedlist.go └── main.go // linkedlist.go 代码如下: package main import "fmt" type LinkNode struct { // 定义一个节点类 data interface{} // 因为 data 要存任何类型的数据,所以要用空接口 next *LinkNode } type Linkedlist struct{ // 定义一个链表类 head *LinkNode // 定义链表头节点 tail *LinkNode // 定义链表尾节点 } func (p *Linkedlist) HeadInsert(data interface{}){ //头插法; data 要能够接收任何类型的数据,所以要用接口类型 node := &LinkNode{ // 先生成一个节点 data:data, next:nil, } if (p.head == nil && p.tail == nil){ // 此时链表为空 p.tail = node p.head = node // 头尾节点此时都赋值为 node return } node.next = p.head // 链表头作为新生成节点的 next p.head = node // 新的节点成为链表头 } func (p *Linkedlist) TailInsert(data interface{}){ // 尾插法 node := &LinkNode{ // 先生成一个节点 data:data, next:nil, } if (p.head == nil && p.tail == nil){ // 此时链表为空 p.tail = node p.head = node // 头尾节点此时都赋值为 node return } p.tail.next = node // 新生成节点先成为原尾节点的 next p.tail = node // 新节点成为尾节点 } func (p *Linkedlist) Traversal(){ // 遍历链表 node := p.head for (node != nil){ fmt.Println(node.data) node = node.next } } // main.go 代码如下: package main func main(){ var intLink *Linkedlist = new(Linkedlist) for i := 0; i < 10; i++{ // intLink.HeadInsert(i) intLink.TailInsert(i) } intLink.Traversal() } // 头插法编译后运行结果如下: [root@NEO project]# go build -o bin/example01_interface_linkedlist go_dev/day06/example01_interface_linkedlist [root@NEO project]# bin/example01_interface_linkedlist 9 8 7 6 5 4 3 2 1 0 [root@NEO project]#
interface{},接口中一个方法也没有,所以任何类型都实现了空接口,也就是任何变量都可以赋值给空接口。
变量slice和接口slice之间赋值操作,要用for range 一个个元素赋值
var a []int var b []interface{} // b = a // 不能直接 a 赋值给了
反射
// 反射:可以在运行时动态获取变量的相关信息 Import ("reflect") 反射相关函数: reflect.TypeOf // 获取变量的类型,返回reflect.Type类型 reflect.ValueOf // 获取变量的值,返回reflect.Value类型 reflect.Value.Kind // 获取变量的类别,返回一个常量 reflect.Value.Interface() // 转换成interface{}类型 // 变量 <--> Interface{} <--> reflect.Value
示例代码:
package main import ( "fmt" "reflect" ) type Student struct{ Name string Age int Score float64 } func test(b interface{}){ t := reflect.TypeOf(b) // 获取变量的类型 fmt.Println(t) // main.Student 类型 v := reflect.ValueOf(b) // 返回 reflect.Value 类型,可利用这个 Value 类型 及相对应的方法 对这个变量进行更深入的分析( 是浮点型,就能获取浮点型的值,int型能获取int的值) fmt.Println(v) // {stu01 18 80} k := v.Kind() // 返回 reflect.Value 的类别 fmt.Println(k) // struct iv := v.Interface() // 再把 reflect.Value 类型转化为 接口类型 stu, ok := iv.(Student) // 判断 iv 是不是指向 Student 类型;如果是 就转化为 Student 类型的值 if ok == true { fmt.Printf("%v %T ",stu,stu) // 打印结果: {stu01 18 80} main.Student } } func TestInt(b interface{}){ v := reflect.ValueOf(b) val := v.Int() // 获取int值(如果是 int类型) fmt.Printf("get value of interface{} %d ",val) // 打印结果: get value of interface{} 123 } func main(){ var stu Student = Student{ Name:"stu01", Age:18, Score:80, } test(stu) TestInt(123) } // 运行结果: [root@NEO example02_reflect_01]# go run main/main.go main.Student {stu01 18 80} struct {stu01 18 80} main.Student get value of interface{} 123 [root@NEO example02_reflect_01]#
reflect.Value.Kind()方法返回的常量
const ( Invalid Kind = iota Bool Int Int8 Int16 Int32 Int64 Uint Uint8 Uint16 Uint32 Uint64 Uintptr Float32 Float64 Complex64 Complex128 Array Chan Func Interface Map Ptr Slice String Struct UnsafePointer )
获取变量的值:
reflect.ValueOf(x).Float() // 获取 float 型的值 reflect.ValueOf(x).Int() // 获取 int 型的值 reflect.ValueOf(x).String() // 获取 string 型的值 reflect.ValueOf(x).Bool() // 获取 bool 型的值
通过反射的来改变变量的值:
reflect.Value.SetXX 相关方法,比如: reflect.Value.SetFloat() // 设置浮点数 reflect.Value.SetInt() // 设置整数 reflect.Value.SetString() // 设置字符串
示例代码:
package main import ( "fmt" "reflect" ) func SetVal(data interface{}){ v := reflect.ValueOf(data) // SetVal(&b) 传入的是一个指针,获取到的 val 也是一个指针(指针型的Value) fmt.Println(v) v.Elem().SetInt(10) // v.Elem() 作用即 相当于 *指针 ,即该指针指向变量的 Value (reflect.Value 型,可理解为一个结构体); 把值设置成 10;由于v是一个指针,此处不能直接用 v.SetInt() } func main(){ var b int = 123 SetVal(&b) // 此处不能只传入 b,只传b传入的是复本,修改复本不会改变原来值的大小, 调用 SetInt()时会 panic fmt.Println(b) } // 运行结果: [root@NEO example02_reflect02_setval]# go run main/main.go 0xc000016098 10 [root@NEO example02_reflect02_setval]#
用反射操作结构体
reflect.Value.NumField() // 获取结构体中字段的个数 reflect.Value.Method(n).Call // 来调用结构体中的方法(n表示下标,可理解成第n个方法);.Call 的参数是 reflect.Value 型的切片,返回值也是 reflect.Value 型的切片
示例代码:
package main import ( "fmt" "reflect" ) type Student struct{ Name string `json:"student_name"` Age int Score float64 } func (p *Student) Print(){ fmt.Println("*p ------->",*p) } func (p *Student) Set(name string,age int,score float64){ p.Name = name p.Age = age p.Score = score } func HandleStruct(data interface{}){ fmt.Printf("v:%v T:%T ",data,data) tye := reflect.TypeOf(data) // 指针型的 reflect.Type v := reflect.ValueOf(data) fmt.Printf("value:%v type:%T ",v,v) kd := v.Elem().Kind() if kd != reflect.Struct { // Struct 是 reflect 这个包中的常量;先判断 v的类别 是不是 结构体类型 fmt.Println("struct expected") return } numField := v.Elem().NumField() // reflect.Value.NumField() ---> 获取结构体字段的个数 fmt.Println("struct field number:",numField) for i := 0; i < v.Elem().NumField();i++ { // 获取字段的值: reflect.Value.Field(i) fmt.Printf("i: %d v_T: %T v: %v ",i, v.Elem().Field(i).Kind(), v.Elem().Field(i)) } // 获取tag (json.Marshal()的原理) tag := tye.Elem().Field(0).Tag.Get("json") // tye.Elem() ---> 指针指向变量的 reflect.Type fmt.Println("tag:",tag) numMethod := v.NumMethod() // reflect.Value.NumMethod() ---> 获取结构体的方法数;由于 Set 传入的是 Student 的指,所以是 v.Set() fmt.Println("struct method number:",numMethod) v.Method(0).Call(nil) // 传入 nil params := make([]reflect.Value,3) // 声明并初始化 reflect.Value 型的切片(该切片要作为 .Call() 的参数) params[0] = reflect.ValueOf("neo") // 任何类型的数据经过 reflect.ValueOf()处理之后都会得到相应的 reflect.Value 类型 params[1] = reflect.ValueOf(22) params[2] = reflect.ValueOf(98.5) v.Method(1).Call(params) fmt.Printf("*data v:%v *data T:%T data v:%v data T:%T ",data,data,*(data.(*Student)),data.(*Student)) // 形参 data 是一个接口,调用 HandleStruct() 函数时传入的 &stu 是一个指针,data 是一个指向指针的接口,接口 data 前不能直接加 * 取值, data.(*Student) 通过断言 获取到 data的指针型数据,再利用 *指针 获取指针指向的值 (应该也可以通过 reflect.ValueOf(data).Elem() 来获取指针指向变量的Value型,然后再获取其值) } func main(){ var stu Student = Student{ Name:"stu01", Age:18, Score:85.5, } HandleStruct(&stu) } // 运行结果: [root@NEO example02_reflect_handleStruct]# go run main/main.go v:&{stu01 18 85.5} T:*main.Student value:&{stu01 18 85.5} type:reflect.Value struct field number: 3 i: 0 v_T: reflect.Kind v: stu01 i: 1 v_T: reflect.Kind v: 18 i: 2 v_T: reflect.Kind v: 85.5 tag: student_name struct method number: 2 *p -------> {stu01 18 85.5} *data v:&{neo 22 98.5} *data T:*main.Student data v:{neo 22 98.5} data T:*main.Student [root@NEO example02_reflect_handleStruct]#