golang(6): 接口 & 反射
接口详解
- // 举例: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 := ; i < ; i++ {
- stu := Student{ // 生成 Student 类型的结构体
- Name: fmt.Sprintf("stu%d",rand.Intn()),
- Id:fmt.Sprintf("110%d",rand.Int()),
- Age: rand.Intn(),
- }
- stuslice = append(stuslice,stu) // Student 类型的结构体添加到 stuslice 切片中
- }
- for _, v := range stuslice {
- fmt.Println(v)
- }
- fmt.Println("\n")
- 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 }
- {stu59 }
- {stu25 }
- {stu0 }
- {stu62 }
- {stu74 }
- {stu37 }
- {stu66 }
- {stu47 }
- {stu88 }
- {stu0 }
- {stu25 }
- {stu37 }
- {stu47 }
- {stu59 }
- {stu62 }
- {stu66 }
- {stu74 }
- {stu81 }
- {stu88 }
- [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\n",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
- 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\n", i,x)
- case float64:
- fmt.Printf("param #%d is a float64,val is %v\n", i,x)
- case int,int64:
- fmt.Printf("param #%d is a int,val is %v \n",i,x)
- case nil:
- fmt.Printf("param #%d is nil,val is %v\n", i,x)
- case string:
- fmt.Printf("param #%d is a string,val is %v\n", i,x)
- default:
- fmt.Printf("param #%d’s type is unknown,val is %v\n", i,x)
- }
- }
- }
- func main() {
- var a int
- classifier(a,"abc",3.3)
- }
- // 运行结果:
- [root@NEO example01_interface_assert02]# go run main/main.go
- param # is a int,val is
- param # is a string,val is abc
- param # 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: ,
- }
- 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\n",v.Strings(),v,ok)
- }
- }
- // 运行结果:
- [root@NEO example01_interface_var2interface]# go run main/main.go
- vp implements Strings():mystruct; v:&{mystruct }; 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 := ; i < ; 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
- [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\n",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\n",val) // 打印结果: get value of interface{} 123
- }
- func main(){
- var stu Student = Student{
- Name:"stu01",
- Age:,
- Score:,
- }
- test(stu)
- TestInt()
- }
- // 运行结果:
- [root@NEO example02_reflect_01]# go run main/main.go
- main.Student
- {stu01 }
- struct
- {stu01 } main.Student
- get value of interface{}
- [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() // v.Elem() 作用即 相当于 *指针 ,即该指针指向变量的 Value (reflect.Value 型,可理解为一个结构体); 把值设置成 10;由于v是一个指针,此处不能直接用 v.SetInt()
- }
- func main(){
- var b int =
- SetVal(&b) // 此处不能只传入 b,只传b传入的是复本,修改复本不会改变原来值的大小, 调用 SetInt()时会 panic
- fmt.Println(b)
- }
- // 运行结果:
- [root@NEO example02_reflect02_setval]# go run main/main.go
- 0xc000016098
- [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\n",data,data)
- tye := reflect.TypeOf(data) // 指针型的 reflect.Type
- v := reflect.ValueOf(data)
- fmt.Printf("value:%v type:%T\n",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 := ; i < v.Elem().NumField();i++ { // 获取字段的值: reflect.Value.Field(i)
- fmt.Printf("i: %d v_T: %T v: %v\n",i, v.Elem().Field(i).Kind(), v.Elem().Field(i))
- }
- // 获取tag (json.Marshal()的原理)
- tag := tye.Elem().Field().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().Call(nil) // 传入 nil
- params := make([]reflect.Value,) // 声明并初始化 reflect.Value 型的切片(该切片要作为 .Call() 的参数)
- params[] = reflect.ValueOf("neo") // 任何类型的数据经过 reflect.ValueOf()处理之后都会得到相应的 reflect.Value 类型
- params[] = reflect.ValueOf()
- params[] = reflect.ValueOf(98.5)
- v.Method().Call(params)
- fmt.Printf("*data v:%v *data T:%T data v:%v data T:%T\n",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:,
- Score:85.5,
- }
- HandleStruct(&stu)
- }
- // 运行结果:
- [root@NEO example02_reflect_handleStruct]# go run main/main.go
- v:&{stu01 85.5} T:*main.Student
- value:&{stu01 85.5} type:reflect.Value
- struct field number:
- i: v_T: reflect.Kind v: stu01
- i: v_T: reflect.Kind v:
- i: v_T: reflect.Kind v: 85.5
- tag: student_name
- struct method number:
- *p -------> {stu01 85.5}
- *data v:&{neo 98.5} *data T:*main.Student data v:{neo 98.5} data T:*main.Student
- [root@NEO example02_reflect_handleStruct]#
golang(6): 接口 & 反射的更多相关文章
- Golang通脉之反射
什么是反射 官方关于反射定义: Reflection in computing is the ability of a program to examine its own structure, pa ...
- golang中的反射reflect详解
先重复一遍反射三定律: 1.反射可以将"接口类型变量"转换为"反射类型对象". 2.反射可以将"反射类型对象"转换为"接口类型变量 ...
- [golang note] 接口使用
侵入式接口 √ 在其他一些编程语言中,接口主要是作为不同组件之间的契约存在,即规定双方交互的规约. √ 对契约的实现是强制的,即必须确保用户的确实现了该接口,而实现一个接口,需要从该接口继承. √ 如 ...
- golang(08)接口介绍
原文链接 http://www.limerence2017.com/2019/09/12/golang13/#more 接口简介 golang 中接口是常用的数据结构,接口可以实现like的功能.什么 ...
- 七、golang中接口、反射
一.接口定义 1.定义 interface类型可以定义一组方法,但是这些不需要实现,并且interface不能包含任何变量 package main import ( "fmt" ...
- golang:reflect反射
因为之前一直以C++为主要开发语言,所以刚接触go语言中的reflect时感觉很懵逼,因此决定找资料彻底学习一下. 到底反射是什么? https://blog.golang.org/laws-of-r ...
- golang基础--reflect反射
反射的知识点比较晦涩,后期会对此知识点展开深入的分析及示例代码展示 反射可达大提高程序的灵活性,使得inferface{}有更大的发挥余地 反射使用TypeOf和ValueOf函数从接口中获取目标对象 ...
- Golang之接口(interface)
Golang最重要的接口,,,, package main import ( "fmt" ) //interface类型默认是指针 /* 接口的实现 Golang中的接口,不需要显 ...
- golang中接口interface和struct结构类的分析
再golang中,我们要充分理解interface和struct这两种数据类型.为此,我们需要优先理解type的作用. type是golang语言中定义数据类型的唯一关键字.对于type中的匿名成员和 ...
随机推荐
- 认识HTML语言(CodePen)
认识HTML语言 1.一个网页的加载过程 2.Web技术全览 3.HTML语法 HTML语法(一):标签 HTML语法(四):网页结构 4.HTML常用元素 展示元素 (1)块级元素div (2)内联 ...
- vue 按需加载,缓存,导航守卫
开发中的注意事项:代码性能的优化 1. 减少对第三方的依赖,降低耦合度 2. 加强组件的重复利用率 3. 按需加载 4. 缓存 (尽量发送请求后保存数据) 5. 开发过程中,尽量有着面向对象的思想,这 ...
- Docker关键概念阐述
要了解Docker需要对其体系结构中的几个关键概念有所了解,主要包括image.container.service.swarm.stack等. 在介绍这几个概念时,会使用到一个测试环境,这个测试环境是 ...
- 如何利用css进行网页布局
一.单列布局(类似于搜狐网站) 如: 代码为: 二.两列布局 1.固定宽度 代码为: 2.自适应 代码为: 三.三列布局 代码为: 四.混合布局 就是在前面的基础上,在进行划分块 如: 代码为:
- Struts2类数据封装
- pandas数据分析案例
1.数据分析步骤 ''' 数据分析步骤: 1.先加载数据 pandas.read_cvs("path") 2.查看数据详情 df.info() ,df.describe() ,df ...
- 阶段3 3.SpringMVC·_05.文件上传_3 文件上传之Springmvc方式上传原理分析
需要配置文件解析器这个对象 id配置时候必须叫做mutipartResolver 最终
- postman提交数组格式方式
提交数组格式数据,对应的服务器端接收的是@RequestBody 和对应的接收值
- python programming GUI综合实战(在GUI上画图)
import os import platform import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5 ...
- [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 ...