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 ...
随机推荐
- Iris Classification on Tensorflow
Iris Classification on Tensorflow Neural Network formula derivation \[ \begin{align} a & = x \cd ...
- Codeforces 980E The Number Games - 贪心 - 树状数组
题目传送门 传送点I 传送点II 传送点III 题目大意 给定一颗有$n$个点的树,$i$号点的权值是$2^{i}$要求删去$k$个点,使得剩下的点仍然连通,并且总权值和最大,问删去的所有点的编号. ...
- uniGUI试用笔记(十)
今天用LoadRunner对uniGUI的Standalone模式的程序进行了一次压力测试,程序采用三层模式,将应用服务器与Web服务器分离,由于条件限制,数据库.应用服务和Web服务都部署在同一条云 ...
- Kettle 连接 Oracle 问题总结
一. Driver class 'oracle.jdbc.driver.OracleDriver' could not be found, make sure the 'Oracle' driver ...
- SpringBoot 整合携程Apollo配置管理中心
携程官网对apollo的使用讲解了很多种方式的使用,但是感觉一些细节还是没讲全,特别是eureka配置中心地址的配置 这里对springboot整合apollo说一下 >SpringBoot启动 ...
- 第一章(欢迎进入node.js世界)
本章内容 1:Node.js是什么 2:服务器端javascript 3:node的异步和事件触发本质 4:node为谁而生 5:node程序示例 1.1 node.js他的首次亮相是在2009年,非 ...
- 常用的 Linux 命令
列出文件列表:ls [参数 -a -l]创建目录和移除目录:mkdir rmdir用于显示文件后几行内容:tail打包:tar -xvf打包并压缩:tar -zcvf查找字符串:grep显示当前所在目 ...
- windows 如何不显示带点的文件名、文件夹?
这些以点开头的文件夹和文件是为了在 linux 中隐藏的.但是在 windows 中如何不显示他们? 资源管理器: CMD:
- Lintcode521-Remove Duplicate Numbers in Array-Easy
Description Given an array of integers, remove the duplicate numbers in it. You should: Do it in pla ...
- Java—网络编程基础
URL的应用: 获得网页源代码可以用字节流.字符流,流的获取可以用url.openStream(),也可以用con.getInputStream(): 字节流: URL url = new URL(& ...