接口详解

  1. // 举例:sort包中的 Sort 函数,如下:
  2. func Sort(data Interface)
  3. 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.
  4. Sort data 进行排序。 它调用一次 data.Len 来决定排序的长度 n,调用 data.Less data.Swap 的开销为 O(n*log(n))。此排序为不稳定排序。)
  5.  
  6. type Interface interface {
  7. // Len is the number of elements in the collection.
  8. Len() int
  9. // Less reports whether the element with
  10. // index i should sort before the element with index j.
  11. Less(i, j int) bool
  12. // Swap swaps the elements with indexes i and j.
  13. Swap(i, j int)
  14. }

举例来扩展 Sort 函数的功能,代码如下:

  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "sort"
  6. "math/rand"
  7. )
  8.  
  9. type Student struct {
  10. Name string
  11. Id string
  12. Age int
  13. }
  14.  
  15. type StudentSlice []Student // 自定义一个切片类型 StudentSlice,切片中的元素是 Student 类型
  16.  
  17. func (p StudentSlice) Len() int {
  18. return len(p)
  19. }
  20.  
  21. func (p StudentSlice) Less(i, j int) bool {
  22. return p[i].Name < p[j].Name
  23. }
  24.  
  25. func (p StudentSlice) Swap(i, j int) {
  26. p[i], p[j] = p[j], p[i]
  27. }
  28.  
  29. // StudentSlice 类型完全实现了 sort.Sort() 形参中的 Interface 类型,那么 就可以用 sort.Sort() 函数对 StudentSlice 类型的数据进行排序
  30.  
  31. func main(){
  32. var stuslice StudentSlice
  33. for i := ; i < ; i++ {
  34. stu := Student{ // 生成 Student 类型的结构体
  35. Name: fmt.Sprintf("stu%d",rand.Intn()),
  36. Id:fmt.Sprintf("110%d",rand.Int()),
  37. Age: rand.Intn(),
  38. }
  39. stuslice = append(stuslice,stu) // Student 类型的结构体添加到 stuslice 切片中
  40. }
  41.  
  42. for _, v := range stuslice {
  43. fmt.Println(v)
  44. }
  45.  
  46. fmt.Println("\n")
  47.  
  48. sort.Sort(stuslice) // 由于stuslice 类型实现了Interface这个接口,那么就能调用 sort.Sort() 对其进行排序
  49.  
  50. for _, v := range stuslice {
  51. fmt.Println(v)
  52. }
  53. }
  54.  
  55. // 编译后运行结果如下:
  56. [root@NEO project]# go build -o bin/example01_interface_extend go_dev/day06/example01_interface_extend/main
  57. [root@NEO project]# bin/example01_interface_extend
  58. {stu81 }
  59. {stu59 }
  60. {stu25 }
  61. {stu0 }
  62. {stu62 }
  63. {stu74 }
  64. {stu37 }
  65. {stu66 }
  66. {stu47 }
  67. {stu88 }
  68.  
  69. {stu0 }
  70. {stu25 }
  71. {stu37 }
  72. {stu47 }
  73. {stu59 }
  74. {stu62 }
  75. {stu66 }
  76. {stu74 }
  77. {stu81 }
  78. {stu88 }
  79. [root@NEO project]#
  80.  
  81. // 任何类型,只要实现了它的规范(接口),就可以调用它的函数来排序

断言

  1. // 示例代码:
  2. package main
  3.  
  4. import (
  5. "fmt"
  6. )
  7.  
  8. func main(){
  9. var a interface{} // 定义一个空接口
  10. var b int
  11. a = b
  12. c := a.(int) // 断言;把 接口a 转成 int 型
  13. fmt.Printf("%d %T\n",c,c)
  14.  
  15. d,ok := a.(string) // 断言(加判断);检查是否能转成 string 类型,并带判断
  16. if ok == false {
  17. fmt.Println("convert failed")
  18. return
  19. }
  20. fmt.Println("%d :string type",d)
  21. }
  22.  
  23. // 运行结果:
  24. [root@NEO example01_interface_assert]# go run main/main.go
  25. int
  26. convert failed
  27. [root@NEO example01_interface_assert]#

类型断言:(采用type switch方式)

写一个函数判断传入参数的类型

  1. // 示例代码:
  2. package main
  3.  
  4. import "fmt"
  5.  
  6. func classifier(items...interface{}){
  7. for i,x := range(items){
  8. switch x.(type){ // 接口.(type) 要和 switch 一起用
  9. case bool:
  10. fmt.Printf("param #%d is a bool,val is %v\n", i,x)
  11. case float64:
  12. fmt.Printf("param #%d is a float64,val is %v\n", i,x)
  13. case int,int64:
  14. fmt.Printf("param #%d is a int,val is %v \n",i,x)
  15. case nil:
  16. fmt.Printf("param #%d is nil,val is %v\n", i,x)
  17. case string:
  18. fmt.Printf("param #%d is a string,val is %v\n", i,x)
  19. default:
  20. fmt.Printf("param #%d’s type is unknown,val is %v\n", i,x)
  21. }
  22. }
  23. }
  24.  
  25. func main() {
  26. var a int
  27. classifier(a,"abc",3.3)
  28. }
  29.  
  30. // 运行结果:
  31. [root@NEO example01_interface_assert02]# go run main/main.go
  32. param # is a int,val is
  33. param # is a string,val is abc
  34. param # is a float64,val is 3.3
  35. [root@NEO example01_interface_assert02]#

判断一个变量是否实现了指定接口

  1. // 示例代码:
  2. package main
  3.  
  4. import "fmt"
  5.  
  6. type Stringer interface {
  7. Strings() string
  8. }
  9.  
  10. type MyStruct struct{
  11. Name string
  12. Id int
  13. }
  14.  
  15. func (p *MyStruct) Strings() string{
  16. return p.Name
  17. }
  18.  
  19. func main() {
  20. var vp *MyStruct = &MyStruct{
  21. Name:"mystruct",
  22. Id: ,
  23. }
  24.  
  25. var t interface{}
  26. t = vp // 要判断变量是否实现了某个接口,必须要选择该变量赋值给一个接口再调用下面的方法
  27.  
  28. if v,ok := t.(Stringer);ok { // 此处只能 t.(Stringer),即只能用接口去调用 .(接口名) (把变量赋值给接口再判断);返回两个值:接口t指向的变量 和 判断结果(true 或 false)
  29. fmt.Printf("vp implements Strings():%s; v:%v; ok:%v\n",v.Strings(),v,ok)
  30. }
  31. }
  32.  
  33. // 运行结果:
  34. [root@NEO example01_interface_var2interface]# go run main/main.go
  35. vp implements Strings():mystruct; v:&{mystruct }; ok:true
  36. [root@NEO example01_interface_var2interface]#

实现一个通用的链表类

  1. // 目录结构如下:
  2. example01_interface_linkedlist
  3. ├── linkedlist.go
  4. └── main.go
  5.  
  6. // linkedlist.go 代码如下:
  7. package main
  8.  
  9. import "fmt"
  10.  
  11. type LinkNode struct { // 定义一个节点类
  12. data interface{} // 因为 data 要存任何类型的数据,所以要用空接口
  13. next *LinkNode
  14. }
  15.  
  16. type Linkedlist struct{ // 定义一个链表类
  17. head *LinkNode // 定义链表头节点
  18. tail *LinkNode // 定义链表尾节点
  19. }
  20.  
  21. func (p *Linkedlist) HeadInsert(data interface{}){ //头插法; data 要能够接收任何类型的数据,所以要用接口类型
  22. node := &LinkNode{ // 先生成一个节点
  23. data:data,
  24. next:nil,
  25. }
  26.  
  27. if (p.head == nil && p.tail == nil){ // 此时链表为空
  28. p.tail = node
  29. p.head = node // 头尾节点此时都赋值为 node
  30. return
  31. }
  32.  
  33. node.next = p.head // 链表头作为新生成节点的 next
  34. p.head = node // 新的节点成为链表头
  35. }
  36.  
  37. func (p *Linkedlist) TailInsert(data interface{}){ // 尾插法
  38. node := &LinkNode{ // 先生成一个节点
  39. data:data,
  40. next:nil,
  41. }
  42.  
  43. if (p.head == nil && p.tail == nil){ // 此时链表为空
  44. p.tail = node
  45. p.head = node // 头尾节点此时都赋值为 node
  46. return
  47. }
  48.  
  49. p.tail.next = node // 新生成节点先成为原尾节点的 next
  50. p.tail = node // 新节点成为尾节点
  51. }
  52.  
  53. func (p *Linkedlist) Traversal(){ // 遍历链表
  54. node := p.head
  55. for (node != nil){
  56. fmt.Println(node.data)
  57. node = node.next
  58. }
  59. }
  60.  
  61. // main.go 代码如下:
  62. package main
  63.  
  64. func main(){
  65. var intLink *Linkedlist = new(Linkedlist)
  66. for i := ; i < ; i++{
  67. // intLink.HeadInsert(i)
  68. intLink.TailInsert(i)
  69. }
  70.  
  71. intLink.Traversal()
  72. }
  73.  
  74. // 头插法编译后运行结果如下:
  75. [root@NEO project]# go build -o bin/example01_interface_linkedlist go_dev/day06/example01_interface_linkedlist
  76. [root@NEO project]# bin/example01_interface_linkedlist
  77.  
  78. [root@NEO project]#

interface{},接口中一个方法也没有,所以任何类型都实现了空接口,也就是任何变量都可以赋值给空接口。

变量slice和接口slice之间赋值操作,要用for range 一个个元素赋值

  1. var a []int
  2. var b []interface{}
  3. // b = a // 不能直接 a 赋值给了

反射

  1. // 反射:可以在运行时动态获取变量的相关信息
  2. Import ("reflect")
  3.  
  4. 反射相关函数:
  5. reflect.TypeOf // 获取变量的类型,返回reflect.Type类型
  6. reflect.ValueOf // 获取变量的值,返回reflect.Value类型
  7. reflect.Value.Kind // 获取变量的类别,返回一个常量
  8. reflect.Value.Interface() // 转换成interface{}类型
  9.  
  10. // 变量 <--> Interface{} <--> reflect.Value

示例代码:

  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "reflect"
  6. )
  7.  
  8. type Student struct{
  9. Name string
  10. Age int
  11. Score float64
  12. }
  13.  
  14. func test(b interface{}){
  15. t := reflect.TypeOf(b) // 获取变量的类型
  16. fmt.Println(t) // main.Student 类型
  17.  
  18. v := reflect.ValueOf(b) // 返回 reflect.Value 类型,可利用这个 Value 类型 及相对应的方法 对这个变量进行更深入的分析( 是浮点型,就能获取浮点型的值,int型能获取int的值)
  19. fmt.Println(v) // {stu01 18 80}
  20.  
  21. k := v.Kind() // 返回 reflect.Value 的类别
  22. fmt.Println(k) // struct
  23.  
  24. iv := v.Interface() // 再把 reflect.Value 类型转化为 接口类型
  25. stu, ok := iv.(Student) // 判断 iv 是不是指向 Student 类型;如果是 就转化为 Student 类型的值
  26. if ok == true {
  27. fmt.Printf("%v %T\n",stu,stu) // 打印结果: {stu01 18 80} main.Student
  28. }
  29. }
  30.  
  31. func TestInt(b interface{}){
  32. v := reflect.ValueOf(b)
  33. val := v.Int() // 获取int值(如果是 int类型)
  34. fmt.Printf("get value of interface{} %d\n",val) // 打印结果: get value of interface{} 123
  35. }
  36.  
  37. func main(){
  38. var stu Student = Student{
  39. Name:"stu01",
  40. Age:,
  41. Score:,
  42. }
  43. test(stu)
  44.  
  45. TestInt()
  46. }
  47.  
  48. // 运行结果:
  49. [root@NEO example02_reflect_01]# go run main/main.go
  50. main.Student
  51. {stu01 }
  52. struct
  53. {stu01 } main.Student
  54. get value of interface{}
  55. [root@NEO example02_reflect_01]#

reflect.Value.Kind()方法返回的常量

  1. const (
  2. Invalid Kind = iota
  3. Bool
  4. Int
  5. Int8
  6. Int16
  7. Int32
  8. Int64
  9. Uint
  10. Uint8
  11. Uint16
  12. Uint32
  13. Uint64
  14. Uintptr
  15. Float32
  16. Float64
  17. Complex64
  18. Complex128
  19. Array
  20. Chan
  21. Func
  22. Interface
  23. Map
  24. Ptr
  25. Slice
  26. String
  27. Struct
  28. UnsafePointer
  29. )

获取变量的值:

  1. reflect.ValueOf(x).Float() // 获取 float 型的值
  2. reflect.ValueOf(x).Int() // 获取 int 型的值
  3. reflect.ValueOf(x).String() // 获取 string 型的值
  4. reflect.ValueOf(x).Bool() // 获取 bool 型的值

通过反射的来改变变量的值:

  1. reflect.Value.SetXX 相关方法,比如:
  2. reflect.Value.SetFloat() // 设置浮点数
  3. reflect.Value.SetInt() // 设置整数
  4. reflect.Value.SetString() // 设置字符串

示例代码:

  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "reflect"
  6. )
  7.  
  8. func SetVal(data interface{}){
  9. v := reflect.ValueOf(data) // SetVal(&b) 传入的是一个指针,获取到的 val 也是一个指针(指针型的Value)
  10. fmt.Println(v)
  11. v.Elem().SetInt() // v.Elem() 作用即 相当于 *指针 ,即该指针指向变量的 Value (reflect.Value 型,可理解为一个结构体); 把值设置成 10;由于v是一个指针,此处不能直接用 v.SetInt()
  12.  
  13. }
  14.  
  15. func main(){
  16. var b int =
  17. SetVal(&b) // 此处不能只传入 b,只传b传入的是复本,修改复本不会改变原来值的大小, 调用 SetInt()时会 panic
  18. fmt.Println(b)
  19. }
  20.  
  21. // 运行结果:
  22. [root@NEO example02_reflect02_setval]# go run main/main.go
  23. 0xc000016098
  24.  
  25. [root@NEO example02_reflect02_setval]#

用反射操作结构体

  1. reflect.Value.NumField() // 获取结构体中字段的个数
  2. reflect.Value.Method(n).Call // 来调用结构体中的方法(n表示下标,可理解成第n个方法);.Call 的参数是 reflect.Value 型的切片,返回值也是 reflect.Value 型的切片

示例代码:

  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "reflect"
  6. )
  7.  
  8. type Student struct{
  9. Name string `json:"student_name"`
  10. Age int
  11. Score float64
  12. }
  13.  
  14. func (p *Student) Print(){
  15. fmt.Println("*p ------->",*p)
  16. }
  17.  
  18. func (p *Student) Set(name string,age int,score float64){
  19. p.Name = name
  20. p.Age = age
  21. p.Score = score
  22. }
  23.  
  24. func HandleStruct(data interface{}){
  25. fmt.Printf("v:%v T:%T\n",data,data)
  26. tye := reflect.TypeOf(data) // 指针型的 reflect.Type
  27.  
  28. v := reflect.ValueOf(data)
  29. fmt.Printf("value:%v type:%T\n",v,v)
  30. kd := v.Elem().Kind()
  31. if kd != reflect.Struct { // Struct 是 reflect 这个包中的常量;先判断 v的类别 是不是 结构体类型
  32. fmt.Println("struct expected")
  33. return
  34. }
  35.  
  36. numField := v.Elem().NumField() // reflect.Value.NumField() ---> 获取结构体字段的个数
  37. fmt.Println("struct field number:",numField)
  38.  
  39. for i := ; i < v.Elem().NumField();i++ { // 获取字段的值: reflect.Value.Field(i)
  40. fmt.Printf("i: %d v_T: %T v: %v\n",i, v.Elem().Field(i).Kind(), v.Elem().Field(i))
  41. }
  42.  
  43. // 获取tag (json.Marshal()的原理)
  44. tag := tye.Elem().Field().Tag.Get("json") // tye.Elem() ---> 指针指向变量的 reflect.Type
  45. fmt.Println("tag:",tag)
  46.  
  47. numMethod := v.NumMethod() // reflect.Value.NumMethod() ---> 获取结构体的方法数;由于 Set 传入的是 Student 的指,所以是 v.Set()
  48. fmt.Println("struct method number:",numMethod)
  49.  
  50. v.Method().Call(nil) // 传入 nil
  51.  
  52. params := make([]reflect.Value,) // 声明并初始化 reflect.Value 型的切片(该切片要作为 .Call() 的参数)
  53. params[] = reflect.ValueOf("neo") // 任何类型的数据经过 reflect.ValueOf()处理之后都会得到相应的 reflect.Value 类型
  54. params[] = reflect.ValueOf()
  55. params[] = reflect.ValueOf(98.5)
  56. v.Method().Call(params)
  57. fmt.Printf("*data v:%v *data T:%T data v:%v data T:%T\n",data,data,*(data.(*Student)),data.(*Student))
  58. // 形参 data 是一个接口,调用 HandleStruct() 函数时传入的 &stu 是一个指针,data 是一个指向指针的接口,接口 data 前不能直接加 * 取值, data.(*Student) 通过断言 获取到 data的指针型数据,再利用 *指针 获取指针指向的值 (应该也可以通过 reflect.ValueOf(data).Elem() 来获取指针指向变量的Value型,然后再获取其值)
  59. }
  60.  
  61. func main(){
  62. var stu Student = Student{
  63. Name:"stu01",
  64. Age:,
  65. Score:85.5,
  66. }
  67. HandleStruct(&stu)
  68. }
  69.  
  70. // 运行结果:
  71. [root@NEO example02_reflect_handleStruct]# go run main/main.go
  72. v:&{stu01 85.5} T:*main.Student
  73. value:&{stu01 85.5} type:reflect.Value
  74. struct field number:
  75. i: v_T: reflect.Kind v: stu01
  76. i: v_T: reflect.Kind v:
  77. i: v_T: reflect.Kind v: 85.5
  78. tag: student_name
  79. struct method number:
  80. *p -------> {stu01 85.5}
  81. *data v:&{neo 98.5} *data T:*main.Student data v:{neo 98.5} data T:*main.Student
  82. [root@NEO example02_reflect_handleStruct]#

golang(6): 接口 & 反射的更多相关文章

  1. Golang通脉之反射

    什么是反射 官方关于反射定义: Reflection in computing is the ability of a program to examine its own structure, pa ...

  2. golang中的反射reflect详解

    先重复一遍反射三定律: 1.反射可以将"接口类型变量"转换为"反射类型对象". 2.反射可以将"反射类型对象"转换为"接口类型变量 ...

  3. [golang note] 接口使用

    侵入式接口 √ 在其他一些编程语言中,接口主要是作为不同组件之间的契约存在,即规定双方交互的规约. √ 对契约的实现是强制的,即必须确保用户的确实现了该接口,而实现一个接口,需要从该接口继承. √ 如 ...

  4. golang(08)接口介绍

    原文链接 http://www.limerence2017.com/2019/09/12/golang13/#more 接口简介 golang 中接口是常用的数据结构,接口可以实现like的功能.什么 ...

  5. 七、golang中接口、反射

    一.接口定义 1.定义 interface类型可以定义一组方法,但是这些不需要实现,并且interface不能包含任何变量 package main import ( "fmt" ...

  6. golang:reflect反射

    因为之前一直以C++为主要开发语言,所以刚接触go语言中的reflect时感觉很懵逼,因此决定找资料彻底学习一下. 到底反射是什么? https://blog.golang.org/laws-of-r ...

  7. golang基础--reflect反射

    反射的知识点比较晦涩,后期会对此知识点展开深入的分析及示例代码展示 反射可达大提高程序的灵活性,使得inferface{}有更大的发挥余地 反射使用TypeOf和ValueOf函数从接口中获取目标对象 ...

  8. Golang之接口(interface)

    Golang最重要的接口,,,, package main import ( "fmt" ) //interface类型默认是指针 /* 接口的实现 Golang中的接口,不需要显 ...

  9. golang中接口interface和struct结构类的分析

    再golang中,我们要充分理解interface和struct这两种数据类型.为此,我们需要优先理解type的作用. type是golang语言中定义数据类型的唯一关键字.对于type中的匿名成员和 ...

随机推荐

  1. 认识HTML语言(CodePen)

    认识HTML语言 1.一个网页的加载过程 2.Web技术全览 3.HTML语法 HTML语法(一):标签 HTML语法(四):网页结构 4.HTML常用元素 展示元素 (1)块级元素div (2)内联 ...

  2. vue 按需加载,缓存,导航守卫

    开发中的注意事项:代码性能的优化 1. 减少对第三方的依赖,降低耦合度 2. 加强组件的重复利用率 3. 按需加载 4. 缓存 (尽量发送请求后保存数据) 5. 开发过程中,尽量有着面向对象的思想,这 ...

  3. Docker关键概念阐述

    要了解Docker需要对其体系结构中的几个关键概念有所了解,主要包括image.container.service.swarm.stack等. 在介绍这几个概念时,会使用到一个测试环境,这个测试环境是 ...

  4. 如何利用css进行网页布局

    一.单列布局(类似于搜狐网站) 如: 代码为: 二.两列布局 1.固定宽度 代码为: 2.自适应 代码为: 三.三列布局 代码为: 四.混合布局 就是在前面的基础上,在进行划分块 如: 代码为:

  5. Struts2类数据封装

  6. pandas数据分析案例

    1.数据分析步骤 ''' 数据分析步骤: 1.先加载数据 pandas.read_cvs("path") 2.查看数据详情 df.info() ,df.describe() ,df ...

  7. 阶段3 3.SpringMVC·_05.文件上传_3 文件上传之Springmvc方式上传原理分析

    需要配置文件解析器这个对象 id配置时候必须叫做mutipartResolver 最终

  8. postman提交数组格式方式

    提交数组格式数据,对应的服务器端接收的是@RequestBody 和对应的接收值

  9. python programming GUI综合实战(在GUI上画图)

    import os import platform import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5 ...

  10. [LeetCode] 697. Degree of an Array 数组的度

    Given a non-empty array of non-negative integers nums, the degree of this array is defined as the ma ...