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中的匿名成员和 ...
随机推荐
- CI集成Smarty的实现方式
给新伙伴的忠告:不要去想着有多复杂,看一遍绝对就会弄了! 这样集成的目的是什么? 因为我使用过CI和smarty,所以我就按自己的理解讲一下:CI框架在控制器.models方面做的很好,但在多变的视图 ...
- 阿里云服务器ECS装好宝塔 但访问不了面板的解决方法
(SSH) (phpmyadmin) 如果你进入面板里修改了面板端口或FTP端口,记得要在安全组和面板防火墙放行相应端口
- html上传图片后,在页面显示上传的图片
html上传图片后,在页面显示上传的图片 1.html <form class="container" enctype="multipart/form-data&q ...
- [论文理解] FoveaBox: Beyond Anchor-based Object Detector
FoveaBox: Beyond Anchor-based Object Detector Intro 本文是一篇one-stage anchor free的目标检测文章,大体检测思路为,网络分两路, ...
- 二、WebSphere Application Server上部署war包并访问
进入我们was服务器控制台之后我们直接按照下图操作: 2.选择要上传的war包,下一步 3.一直下一步,步骤4注意填好“上下文根”,然后继续下一步,直到完成. 4.点击保存到主配置 5.应用程序> ...
- LC 789. Escape The Ghosts
You are playing a simplified Pacman game. You start at the point (0, 0), and your destination is(tar ...
- RGB颜色透明度转换
100% — FF95% — F290% — E685% — D980% — CC75% — BF70% — B365% — A660% — 9955% — 8C50% — 8045% — 7340% ...
- 数据库开源框架之GreenDAO
主页: https://github.com/greenrobot/greenDAO 配置: 添加以下依赖 * compile 'de.greenrobot:greendao:2.1.0' * com ...
- 判断是否为日期格式 与 判断是否为BigDecimal
import java.text.ParseException;import java.text.SimpleDateFormat; /** * * 说明:判断是否为日期格式 * @param str ...
- Android ADT安装与卸载
Android ADT安装 Eclipse 版本: Eclipse Java EE IDE for Web Developers. Version: Kepler Release Build id: ...