Go语言学习之6 反射详解
1.反射:
定义: 反射就是程序能够在运行时检查变量和值,求出它们的类型。
可以在运行时动态获取变量的相关信息
Import ("reflect")
为什么需要反射?
想象下:如果程序中每个变量都是我们自己定义的,那么在编译时就可以知道变量类型了,但是实际中并非如此,就需要我们在运行时检查变量,求出它的类型。这就需要用到反射。
在 Go 语言中,reflect 实现了运行时反射。reflect 包会帮助识别 interface{} 变量的底层具体类型和具体值。
几个函数:
a. reflect.TypeOf(val),获取变量的类型,返回reflect.Type类型
b. reflect.ValueOf(val),获取变量的值,返回reflect.Value类型
c. reflect.Value.Kind(),获取变量的类别,返回一个常量
d. reflect.Value.Interface(),转换成interface{}类型
变量接口及获取变量值之间的转换:
package main import (
"fmt"
"reflect"
) type Student struct {
Name string
Age int
Score float32
} func test(b interface{}) {
t := reflect.TypeOf(b)
fmt.Println("t: ", t) //t: main.Student v := reflect.ValueOf(b)
fmt.Println("v: ", v) //v: {stu01 18 92} k := v.Kind()
fmt.Println("k: ", k) //k: struct iv := v.Interface()
fmt.Println("iv: ", iv) //iv: {stu01 18 92}
stu, ok := iv.(Student)
if ok {
fmt.Printf("%v %T\n", stu, stu) //{stu01 18 92} main.Student
}
} func main() {
var a Student = Student{
Name: "stu01",
Age: ,
Score: ,
}
test(a)
}
example
package main import (
"fmt"
"reflect"
) func main() { var x float64 = 3.4
fmt.Println("type:", reflect.TypeOf(x)) //type: float64
v := reflect.ValueOf(x)
fmt.Printf("value:%v, type:%T\n", v, v) //value:3.4, type: reflect.Valuetype
fmt.Println("type:", v.Type()) //type: float64
fmt.Println("kind:", v.Kind()) //kind: float64
fmt.Println("value:", v.Float()) //value: 3.4 fmt.Println(v.Interface()) //3.4
fmt.Printf("value is %5.2e\n", v.Interface()) //value is 3.40e+00
y := v.Interface().(float64) //v -> Interface -> float64 -> y
fmt.Println(y) //3.4
}
example2
2. 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
)
Kind返回的常量
3. 获取变量的值:
reflect.ValueOf(x).Float()
reflect.ValueOf(x).Int()
reflect.ValueOf(x).String()
reflect.ValueOf(x).Bool()
4. 通过反射的来改变变量的值
reflect.Value.SetXX相关方法,比如:
reflect.Value.SetFloat(),设置浮点数
reflect.Value.SetInt(),设置整数
reflect.Value.SetString(),设置字符串
练习:(panic: reflect: reflect.Value.SetFloat using unaddressable value)
package main import (
"fmt"
"reflect"
) func main() {
var a float64
fv := reflect.ValueOf(a)
fv.SetFloat(3.3)
fmt.Printf("%v\n", a)
}
程序崩溃了
崩溃的原因:还是值类型和引用类型的原因。v := reflect.ValueOf(x) ,v是x的一个拷贝,修改v,x不会修改!
解决方法:传地址!
package main import (
"fmt"
"reflect"
) func main() {
var a float64 = 1.0 fv := reflect.ValueOf(&a)
fv.Elem().SetFloat(3.3) //相当于var *fv float64; *fv = 3.3
fmt.Printf("%v\n", a) //3.3
}
传地址
其中fv.Elem().Setxx用来获取指针指向的变量,相当于:
var a *int;
*a = 100
5. 用反射操作结构体
a. reflect.Value.NumField() 获取结构体中字段的个数
b. reflect.Value.Method(n).Call 来调用结构体中的方法
func (Value) Call
func (v Value) Call(in []Value) []Value
Call calls the function v with the input arguments in. For example, if len(in) == , v.Call(in) represents the Go call v(in[], in[], in[]). Call panics if v's Kind is not Func. It returns the output results as Values. As in Go, each input argument must be assignable to the type of the function's corresponding input parameter. If v is a variadic function, Call creates the variadic slice parameter itself, copying in the corresponding values. func (Value) Elem
func (v Value) Elem() Value
Elem returns the value that the interface v contains or that the pointer v points to. It panics if v's Kind is not Interface or Ptr. It returns the zero Value if v is nil. func (Value) NumField
func (v Value) NumField() int
NumField returns the number of fields in the struct v. It panics if v's Kind is not Struct.
Elem, NumField,Call官方解释
package main import (
"encoding/json"
"fmt"
"reflect"
) type Student struct {
Name string `json:"student_name"`
Age int
Score float32
Sex string
} func (s Student) Print() {
fmt.Println("---start----")
fmt.Println(s)
fmt.Println("---end----")
} func (s Student) Set(name string, age int, score float32, sex string) {
s.Name = name
s.Age = age
s.Score = score
s.Sex = sex
fmt.Println("set -----------")
} func TestStruct(a interface{}) {
tye := reflect.TypeOf(a)
val := reflect.ValueOf(a)
kd := val.Kind()
// fmt.Println(tye, val, kd, val.Elem().Kind()) //*main.Student &{stu01 18 92.8 } ptr struct if kd != reflect.Ptr && val.Elem().Kind() == reflect.Struct {
fmt.Println("expect struct")
return
} num := val.Elem().NumField() //获取val
val.Elem().Field().SetString("stu1000")
for i := ; i < num; i++ {
fmt.Printf("%d %v\n", i, val.Elem().Field(i).Kind())
} fmt.Printf("struct has %d fields\n", num) tag := tye.Elem().Field().Tag.Get("json")
fmt.Printf("tag=%s\n", tag) //tag=student_name numOfMethod := val.Elem().NumMethod() //
fmt.Printf("struct has %d methods\n", numOfMethod)
var params []reflect.Value
val.Elem().Method().Call(params)
} func main() {
var a Student = Student{
Name: "stu01",
Age: ,
Score: 92.8,
} result, _ := json.Marshal(a)
fmt.Println("json result:", string(result)) TestStruct(&a)
fmt.Println(a)
}
example
6. 反射中调用函数
Go中的函数是可以像普通的 int、float 等类型变量那样作为值的。例如:
package main import "fmt" func Print() {
fmt.Println("Hello world!")
} func main() {
f := Print
f()
}
函数作为变量
和函数作为变量类似,在反射中函数和方法的类型(Type)都是 reflect.Func,如果要调用函数的话,可以通过 Value的Call()方法。
package main import (
"fmt"
"reflect"
) func Print() {
fmt.Println("Hello world!")
} func main() {
f := Print
fv := reflect.ValueOf(f)
fmt.Println("fv is reflect.Func? ", fv.Kind() == reflect.Func) //fv is reflect.Func? true
fv.Call(nil) //Hello world!
}
测试reflect.Func
Call函数的定义:func (v Value) Call(in []Value) []Value
Value 的 Call() 方法的参数是一个 Value 的 slice,对应的反射函数类型的参数,返回值也是一个 Value 的 slice,同样对应反射函数类型的返回值。
package main import (
"fmt"
"reflect"
) func Print(str string) string {
str = fmt.Sprintf("hello %s", str)
return str
} func main() {
fv := reflect.ValueOf(Print)
params := make([]reflect.Value, ) // 传给Print的参数
params[] = reflect.ValueOf("zhangsan") // 参数设置为"zhangsan"
rs := fv.Call(params) // rs作为结果接受函数的返回值
//result: hello zhangsan
fmt.Println("result:", rs[].Interface().(string)) // rs[0].Interface() ok
}
example
7. 反射中调用方法
函数和方法可以说其实本质上是相同的,只不过方法与一个“对象”进行了“绑定”,方法是“对象”的一种行为,这种行为是对于这个“对象”的一系列操作,例如修改“对象”的某个属性。
package main import (
"fmt"
"reflect"
) type Student struct {
Name string
Age int
} func (s *Student) SetName(name string) {
s.Name = name
} func (s *Student) SetAge(age int) {
s.Age = age
} func (s *Student) Print() string {
str := fmt.Sprintf("%s is %d years old.", s.Name, s.Age)
return str
} func main() {
stu := &Student {
Name:"zhangsan",
Age:,
} ref := reflect.ValueOf(stu)
fmt.Println("Before:", ref.MethodByName("Print").Call(nil)[]) //Before: zhangsan is 20 years old. params := make([]reflect.Value, ) params[] = reflect.ValueOf("lisi")
ref.MethodByName("SetName").Call(params)
fmt.Println(stu) //&{lisi 20} params[] = reflect.ValueOf()
ref.MethodByName("SetAge").Call(params) fmt.Println(stu) //&{lisi 22}
fmt.Println("After:", ref.MethodByName("Print").Call(nil)[]) //After: lisi is 22 years old.
}
example
练习1:
package main import (
"fmt"
"reflect"
) type NotknownType struct {
s1 string
s2 string
s3 string
} func (n NotknownType) String() string {
return n.s1 + "-" + n.s2 + "-" + n.s3
} var secret interface{} = NotknownType{"Ada", "Go", "Oberon"} func main() {
value := reflect.ValueOf(secret)
typ := reflect.TypeOf(secret)
fmt.Println(typ) // Main.NotknownType knd := value.Kind()
fmt.Println(knd) // struct for i := ; i < value.NumField(); i++ {
fmt.Printf("Field %d: %v\n", i, value.Field(i))
//value.Field(i).SetString("C#")
} results := value.Method().Call(nil)
fmt.Println(results) // [Ada - Go - Oberon]
}
练习
练习2:通过反射操作结构体
package main import (
"fmt"
"reflect"
) type NotknownType struct {
s1 string
s2 string
s3 string
} func (n NotknownType) String() string {
return n.s1 + "-" + n.s2 + "-" + n.s3
} var secret interface{} = NotknownType{"Ada", "Go", "Oberon"} func main() {
value := reflect.ValueOf(secret)
typ := reflect.TypeOf(secret)
fmt.Println(typ) // Main.NotknownType knd := value.Kind()
fmt.Println(knd) // struct for i := ; i < value.NumField(); i++ {
fmt.Printf("Field %d: %v\n", i, value.Field(i))
//value.Field(i).SetString("C#")
} results := value.Method().Call(nil)
fmt.Println(results) // [Ada - Go - Oberon]
}
example
练习3:通过反射修改结构体
package main import (
"fmt"
"reflect"
) type T struct {
A int
B string
} func main() {
t := T{, "skidoo"}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type() //main.T
for i := ; i < s.NumField(); i++ {
f := s.Field(i)
fmt.Printf("%d: %s %s = %v\n", i,
typeOfT.Field(i).Name, f.Type(), f.Interface())
}
s.Field().SetInt()
s.Field().SetString("Sunset Strip")
fmt.Println("t is now", t) //t is now {77 Sunset Strip}
}
example
建议:反射是 Go 语言中非常强大和高级的概念,我们应该小心谨慎地使用它。使用反射编写清晰和可维护的代码是十分困难的。你应该尽可能避免使用它,只在必须用到它时,才使用反射。
图书管理系统v2版本开发:
实现一个图书管理系统v2,具有以下功能:
a. 增加用户登录、注册功能
b. 增加借书过期的图书界面
c. 增加显示热门图书的功能,被借次数最多的top10
d. 增加查看某个人的借书记录的功能
参考文献:
- https://studygolang.com/articles/13178 (Go 系列教程 - 反射)
- https://golang.org/pkg/reflect/
- https://www.cnblogs.com/52php/p/6337420.html
Go语言学习之6 反射详解的更多相关文章
- Go语言学习之8 goroutine详解、定时器与单元测试
主要内容: 1.Goroutine2. Chanel3. 单元测试 1. Goroutine Go 协程(Goroutine)(轻量级的线程,开线程没有数量限制). (1)进程和线程 A. 进程是 ...
- [深入学习Web安全](5)详解MySQL注射
[深入学习Web安全](5)详解MySQL注射 0x00 目录 0x00 目录 0x01 MySQL注射的简单介绍 0x02 对于information_schema库的研究 0x03 注射第一步—— ...
- Shell学习之Bash变量详解(二)
Shell学习之Bash变量详解 目录 Bash变量 Bash变量注意点 用户自定义变量 环境变量 位置参数变量 预定义变量 Bash变量 用户自定义变量:在Bash中由用户定义的变量. 环境变量:这 ...
- Asp.Net MVC学习总结之过滤器详解(转载)
来源:http://www.php.cn/csharp-article-359736.html 一.过滤器简介 1.1.理解什么是过滤器 1.过滤器(Filters)就是向请求处理管道中注入额外的 ...
- C#反射の反射详解
C#反射の反射详解(点击跳转)C#反射の反射接口(点击跳转)C#反射反射泛型接口(点击跳转)C#反射の一个泛型反射实现的网络请求框架(点击跳转) 一.什么是反射 反射(Reflection):这是.N ...
- Linux学习之用户配置文件详解(十四)
Linux学习之用户配置文件详解 目录 用户信息文件/etc/password 影子文件/etc/shadow 组信息文件/etc/group 组密码文件/etc/gshadow 用户信息文件/etc ...
- [转载]springmvc学习之@ModelAttribute运用详解
spring学习之@ModelAttribute运用详解 链接
- expect学习笔记及实例详解【转】
1. expect是基于tcl演变而来的,所以很多语法和tcl类似,基本的语法如下所示:1.1 首行加上/usr/bin/expect1.2 spawn: 后面加上需要执行的shell命令,比如说sp ...
- Android 布局学习之——Layout(布局)详解二(常见布局和布局参数)
[Android布局学习系列] 1.Android 布局学习之——Layout(布局)详解一 2.Android 布局学习之——Layout(布局)详解二(常见布局和布局参数) 3.And ...
随机推荐
- shell实现每天0点备份mysql数据库
就两个文件, 本人学识尚浅,不解释,怕大佬喷. back.sh #/bin/bash MYSQLUSER=root MYSQLPWD=lizhenghua DATABASES=zskdb MYSQLD ...
- Oracle使用——oracle用户相关操作
前提 以dba角色登录数据库(普通用户没有操作权限):sqlplus / as sysdba 具体操作 创建用户 创建用户 使用默认表空间创建用户 create user xzgxh identifi ...
- Win10子系统Ubuntu安装llvm+clang
https://apt.llvm.org/ 首先 然后 再然后修改/etc/apt/sources.list,添加下面的东西 然后 参考: https://blog.kowalczyk.info/ar ...
- 论文笔记:Diffusion-Convolutional Neural Networks (传播-卷积神经网络)
Diffusion-Convolutional Neural Networks (传播-卷积神经网络)2018-04-09 21:59:02 1. Abstract: 我们提出传播-卷积神经网络(DC ...
- Druid介绍
Druid (大数据实时统计分析数据存储) Druid 是一个为在大数据集之上做实时统计分析而设计的开源数据存储.这个系统集合了一个面向列存储的层,一个分布式.shared-nothing的架构,和一 ...
- jquery及jquery常用选择器使用
本文为博主原创,未经允许不得转载: 1.jquery强大之处: 容易上手,强大的选择器,解决浏览器的兼容 完善的时间机制,出色的ajax封装,丰富的ui 2.jquery是一个javas ...
- 解决Maven管理项目update Maven时,jre自动变为1.5
本文为博主原创,未经允许不得转载: 在搭建一个maven web项目时,项目已经按步骤搭建完好,之后项目上就报了一个错误. 在控制台看到错误提示如下:Dynamic Web Module 3.0 re ...
- 20165306 实验一Java开发环境的熟悉
Java开发环境的熟悉 实验报告封面 实验内容与步骤 Java开发环境的熟悉-1 1.建立"自己学号exp1"的目录 2.在"自己学号exp1"目录下建立src ...
- 【Django】Django-REST-Framework
[创建简单的API] 1. cmd.exe >django-admin startproject django_rest>cd django_rest\django_rest>pyt ...
- JavaScript中的方法事件和函数的方法的三种方法
js中的很多事件 而事件相对应的就是方法(函数 )那么今天所说的就是这三种方法 已onclick事件为例 1: 基本方法 <div id="a" onclick= ...