函数
Go 语言指针作为函数参数
package main
import "fmt"
func main() {
/* 定义局部变量 */
var a int = 100
var b int= 200
fmt.Printf("交换前 a 的值 : %d\n", a ) //交换前 a 的值 : 100
fmt.Printf("交换前 b 的值 : %d\n", b ) //交换前 b 的值 : 200
/* 调用函数用于交换值
* &a 指向 a 变量的地址
* &b 指向 b 变量的地址
*/
swap(&a, &b);
fmt.Printf("交换后 a 的值 : %d\n", a ) //交换后 a 的值 : 200
fmt.Printf("交换后 b 的值 : %d\n", b ) //交换后 b 的值 : 100
}
func swap(x *int, y *int) {
var temp int
temp = *x /* 保存 x 地址的值 */
*x = *y /* 将 y 赋值给 x */
*y = temp /* 将 temp 赋值给 y */
}
结构体
基本概念
Golang 中没有“类”的概念,Golang 中的结构体和其他语言中的类有点相似
Golang 中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全 部或部分属性时,这时候再用单一的基本数据类型就无法满足需求了,Golang 提供了一种 自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称 struct。 也就是我们可以通过 struct 来定义自己的类型了
type 关键词自定义类型和类型别名
Golang 中通过 type 关键词定义一个结构体
自定义类型
在 Go 语言中有一些基本的数据类型,如 string、整型、浮点型、布尔等数据类型, Go 语 言中可以使用 type 关键字来定义自定义类型。
type myInt int
类型别名
type TypeAlias = Type
自定义类型和类型别名的区别
type myInt int
type myFloat = float64
func main() {
var a myInt = 10
fmt.Printf("%v %T\n", a, a) //10 main.myInt
var b myFloat = 12.3
fmt.Printf("%v %T", b, b) //12.3 float64
}
结果显示 a 的类型是 main.newInt,表示 main 包下定义的 newInt 类型。b 的类型是 int 类型。
结构体定义初始化
结构体的定义
使用 type 和 struct 关键字来定义结构体,具体代码格式如下
type 类型名 struct {
字段名 字段类型
字段名 字段类型
…
}
type person struct {
name string
city string
age int8
}
结构体实例化
只有当结构体实例化时,才会真正地分配内存。也就是必须实例化后才能使用结构体的字段
type Person struct {
Name string
Age int
Sex string
}
第一种
var p1 Person //实例化Person结构体
p1.name = "张三"
p1.sex = "男"
p1.age = 20
fmt.Printf("值:%v 类型:%T\n", p1, p1) //值:{张三 20 男} 类型:main.Person
fmt.Printf("值:%#v 类型:%T", p1, p1) //值:main.Person{name:"张三", age:20, sex:"男"} 类型:main.Person
var p2 = new(Person)
p2.Name = "李四"
p2.Age = 20
p2.Sex = "男"
(*p2).Name = "王五"
fmt.Printf("值:%#v 类型:%T\n", p2, p2) //值:&main.Person{Name:"李四", Age:20, Sex:"男"} 类型:*main.Person
var p3 = &Person{}
p3.Name = "赵四"
p3.Age = 23
p3.Sex = "男"
fmt.Printf("值:%#v 类型:%T\n", p3, p3) //值:&main.Person{Name:"赵四", Age:23, Sex:"男"} 类型:*main.Person
var p4 = Person{
Name: "哈哈",
Age: 20,
Sex: "男",
}
fmt.Printf("值:%#v 类型:%T\n", p4, p4) //值:main.Person{Name:"哈哈", Age:20, Sex:"男"} 类型:main.Person
var p5 = &Person{
Name: "王麻子",
Age: 20,
Sex: "男",
}
fmt.Printf("值:%#v 类型:%T\n", p5, p5) //值:&main.Person{Name:"王麻子", Age:20, Sex:"男"} 类型:*main.Person
var p6 = &Person{
Name: "王麻子",
}
fmt.Printf("值:%#v 类型:%T\n", p6, p6) //值:&main.Person{Name:"王麻子", Age:0, Sex:""} 类型:*main.Person
var p7 = &Person{
"张三",
20,
"男",
}
fmt.Printf("值:%#v 类型:%T\n", p7, p7) //值:&main.Person{Name:"张三", Age:20, Sex:"男"} 类型:*main.Person
值类型
type Person struct {
Name string
Age int
Sex string
}
func main() {
var p1 = Person{
Name: "哈哈",
Age: 20,
Sex: "男",
}
p2 := p1
p2.Name = "李四"
fmt.Printf("%#v\n", p1) //main.Person{Name:"哈哈", Age:20, Sex:"男"}
fmt.Printf("%#v", p2) //main.Person{Name:"李四", Age:20, Sex:"男"}
结构体方法
值类型的接收者
当方法作用于值类型接收者时,Go 语言会在代码运行时将接收者的值复制一份。在值类型 接收者的方法中可以获取接收者的成员值,但修改操作只是针对副本,无法修改接收者变量 本身。
type Person struct {
Name string
Age int
Sex string
height int
}
func (p Person) PrintInfo() {
fmt.Printf("姓名:%v 年龄:%v\n", p.Name, p.Age)
}
func main() {
var p1 = Person{
Name: "张三",
Age: 20,
Sex: "男",
}
p1.PrintInfo() //姓名:张三 年龄:20
var p2 = Person{
Name: "李四",
Age: 30,
Sex: "男",
}
p2.PrintInfo() //姓名:李四 年龄:30
p1.PrintInfo() //姓名:张三 年龄:20
指针类型的接收者
指针类型的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针 的任意成员变量,在方法结束后,修改都是有效的。这种方式就十分接近于其他语言中面向 对象中的 this 或者 self。
type Person struct {
Name string
Age int
Sex string
height int
}
// PrintInfo aa
func (p Person) PrintInfo() {
fmt.Printf("姓名:%v 年龄:%v\n", p.Name, p.Age)
}
// SetInfo aa
func (p *Person) SetInfo(name string, age int) {
p.Name = name
p.Age = age
}
func main() {
var p1 = Person{
Name: "张三",
Age: 20,
Sex: "男",
}
p1.PrintInfo() //姓名:张三 年龄:20
p1.SetInfo("李四", 34)
p1.PrintInfo() //姓名:李四 年龄:34
}
实例化的指针类型的接收者,不相互影响
type Person struct {
Name string
Age int
Sex string
height int
}
func (p Person) PrintInfo() {
fmt.Printf("姓名:%v 年龄:%v\n", p.Name, p.Age)
}
func (p *Person) SetInfo(name string, age int) {
p.Name = name
p.Age = age
}
func main() {
var p1 = Person{
Name: "张三",
Age: 20,
Sex: "男",
}
p1.PrintInfo() //姓名:张三 年龄:20
var p2 = Person{
Name: "王五",
Age: 22,
Sex: "男",
}
p1.SetInfo("李四", 34)
p1.PrintInfo() //姓名:李四 年龄:34
p2.PrintInfo() //姓名:王五 年龄:22
}
给任意类型添加方法
在 Go 语言中,接收者的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法。
我们基于内置的 int 类型使用 type 关键字可以定义新的自定义类型,然后为我们 的自定义类型添加方法
type MyInt int
func (m MyInt) PrintInfo() {
fmt.Println("我是自定义类型里面的自定义方法")
}
func main() {
var a MyInt = 20
a.PrintInfo()
}
结构体的匿名字段
结构体允许其成员字段在声明时没有字段名而只有类型,这种没有名字的字段就称为匿名字段
匿名字段默认采用类型名作为字段名,结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个
type Person struct {
string
int
}
func main() {
// 匿名字段
p := Person{
"张三",
20,
}
fmt.Println(p) //{张三 20}
}
结构体的字段类型
结构体的字段类型可以是:基本数据类型、也可以是切片、Map 以及结构体
如果结构体的字段类型是: 指针,slice,和map的零值都是 nil ,即还没有分配空间
如果需要使用这样的字段,需要先make,才能使用.
type Person struct {
Name string
Age int
Hobby []string
map1 map[string]string
}
func main() {
var p Person
p.Name = "张三"
p.Age = 20
p.Hobby = make([]string, 3, 6)
p.Hobby[0] = "写代码"
p.Hobby[1] = "打篮球"
p.Hobby[2] = "睡觉"
p.map1 = make(map[string]string)
p.map1["address"] = "北京"
p.map1["phone"] = "1324325325"
fmt.Printf("%#v\n", p)
//main.Person{Name:"张三", Age:20, Hobby:[]string{"写代码", "打篮球", "睡觉"},
fmt.Printf("%v", p.Hobby)
//map1:map[string]string{"address":"北京", "phone":"1324325325"}}
[写代码 打篮球 睡觉]
}
结构体嵌套
基本
type User struct {
Username string
Password string
Address Address //表示User结构体嵌套Address结构体
}
type Address struct {
Name string
Phone string
City string
}
func main() {
var u User
u.Username = "itying"
u.Password = "1234567"
u.Address.Name = "张三"
u.Address.Phone = "15201671234"
u.Address.City = "北京"
fmt.Printf("%#v", u) //main.User{Username:"itying", Password:"1234567", Address:main.Address{Name:"张三", Phone:"15201671234", City:"北京"}}
}
嵌套匿名结构体
type User struct {
Username string
Password string
Address
}
type Address struct {
Name string
Phone string
City string
}
func main() {
var u User
u.Username = "itying"
u.Password = "1234567"
u.Address.Name = "张三"
u.Address.Phone = "15201671234"
u.Address.City = "北京"
u.City = "上海" //当访问结构体成员时会先在结构体中查找该字段,找不到再去匿名结构体中查找。
fmt.Printf("%#v\n", u) //main.User{Username:"itying", Password:"1234567", Address:main.Address{Name:"张三", Phone:"15201671234", City:"上海"}}
fmt.Println(u.Address.Phone) //15201671234
fmt.Println(u.Phone) //15201671234
}
嵌套匿名结构体
当访问结构体成员时会先在结构体中查找该字段,找不到再去匿名结构体中查找。
type User struct {
Username string
Password string
AddTime string
Address
}
type Address struct {
Name string
Phone string
City string
AddTime string
}
func main() {
var u User
u.Username = "itying"
u.Password = "1234567"
u.Address.Name = "张三"
u.Address.Phone = "15201671234"
u.Address.City = "北京"
u.City = "上海" //当访问结构体成员时会先在结构体中查找该字段,找不到再去匿名结构体中查找。
u.AddTime = "2020-05-1"
u.Address.AddTime = "2020-06-1"
fmt.Printf("%#v\n", u)
//main.User{Username:"itying", Password:"1234567", AddTime:"2020-05-1", Address:main.Address{Name:"张三", Phone:"15201671234", City:"上海", AddTime:"2020-06-1"}}
}
关于嵌套结构体的字段名冲突
Username string
Password string
Address
Email
}
type Address struct {
Name string
Phone string
City string
AddTime string
}
type Email struct {
Account string
AddTime string
}
func main() {
var u User
u.Username = "itying"
u.Password = "1234567"
u.Address.Name = "张三"
u.Address.Phone = "15201671234"
u.Address.City = "北京"
u.City = "上海" //当访问结构体成员时会先在结构体中查找该字段,找不到再去匿名结构体中查找。
u.Address.AddTime = "2020-05-1"
u.Email.AddTime = "2020-06-1"
fmt.Printf("%#v\n", u)
}
继承
基本
type Animal struct {
Name string
}
func (a Animal) run() {
fmt.Printf("%v 在运动\n", a.Name)
}
//子结构体
type Dog struct {
Age int
Animal //结构体嵌套 继承
}
func (d Dog) wang() {
fmt.Printf("%v 在旺旺\n", d.Name)
}
func main() {
var d = Dog{
Age: 20,
Animal: Animal{
Name: "阿奇",
},
}
d.run() //阿奇 在运动
d.wang() //阿奇 在旺旺
}
指针
type Animal struct {
Name string
}
func (a Animal) run() {
fmt.Printf("%v 在运动\n", a.Name)
}
//子结构体
type Dog struct {
Age int
*Animal //结构体嵌套 继承
}
func (d Dog) wang() {
fmt.Printf("%v 在旺旺\n", d.Name)
}
func main() {
var d = Dog{
Age: 20,
Animal: &Animal{
Name: "阿奇",
},
}
d.run() //阿奇 在运动
d.wang() //阿奇 在旺旺
}
结构体与json
基本
type Student struct {
ID int
Gender string
Name string //私有属性不能被json包访问
Sno string
}
func main() {
var s1 = Student{
ID: 12,
Gender: "男",
Name: "李四",
Sno: "s0001",
}
fmt.Printf("%#v\n", s1) //main.Student{ID:12, Gender:"男", Name:"李四", Sno:"s0001"}
jsonByte, _ := json.Marshal(s1)
fmt.Printf("%v", jsonByte)
jsonStr := string(jsonByte)
fmt.Printf("%v", jsonStr) //{"ID":12,"Gender":"男","Name":"李四","Sno":"s0001"}
}
type Student struct {
ID int
Gender string
Name string //私有属性不能被json包访问
Sno string
}
func main() {
//json字符串
var str = `{"ID":12,"Gender":"男","Name":"李四","Sno":"s0001"}`
var s1 Student
err := json.Unmarshal([]byte(str), &s1)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%#v\n", s1)
fmt.Println(s1.Name)
}
结构体标签
type Student struct {
Id int `json:"id"`
Gender string `json:"gender"`
Name string `json:"name"` //私有属性不能被json包访问
Sno string `json:"sno"`
}
func main() {
var s1 = Student{
Id: 12,
Gender: "男",
Name: "李四",
Sno: "s0001",
}
fmt.Printf("%#v\n", s1) //main.Student{Id:12, Gender:"男", Name:"李四", Sno:"s0001"}
jsonByte, _ := json.Marshal(s1)
jsonStr := string(jsonByte)
fmt.Printf("%v", jsonStr) //{"id":12,"gender":"男","name":"李四","sno":"s0001"}
}
type Student struct {
Id int
Gender string
Name string
}
Class 班级
type Class struct {
Title string
Students []Student
}
func main() {
c := Class{
Title: "001班",
Students: make([]Student, 0),
}
for i := 1; i <= 10; i++ {
s := Student{
Id: i,
Gender: "男",
Name: fmt.Sprintf("stu_%v", i),
}
c.Students = append(c.Students, s)
}
// fmt.Println(c)
strByte, err := json.Marshal(c)
if err != nil {
fmt.Println(err)
} else {
strJson := string(strByte)
fmt.Println(strJson)
}
}
type Student struct {
ID int
Gender string
Name string
}
//Class 班级
type Class struct {
Title string
Students []Student
}
func main() {
str := `{"Title":"001班","Students":[{"Id":1,"Gender":"男","Name":"stu_1"},{"Id":2,"Gender":"男","Name":"stu_2"},{"Id":3,"Gender":"男","Name":"stu_3"},{"Id":4,"Gender":"男","Name":"stu_4"},{"Id":5,"Gender":"男","Name":"stu_5"},{"Id":6,"Gender":"男","Name":"stu_6"},{"Id":7,"Gender":"男","Name":"stu_7"},{"Id":8,"Gender":"男","Name":"stu_8"},{"Id":9,"Gender":"男","Name":"stu_9"},{"Id":10,"Gender":"男","Name":"stu_10"}]}`
var c = &Class{}
err := json.Unmarshal([]byte(str), c)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%#v\n", c)
fmt.Printf("%v", c.Title)
}
}
接口
Golang 中的接口
Golang 中的接口是一种抽象数据类型,Golang 中接口定义了对象的行为规范,只定义规范 不实现。接口中定义的规范由具体的对象来实现
type 接口名 interface{
方法名 1( 参数列表 1 ) 返回值列表 1
方法名 2( 参数列表 2 ) 返回值列表 2
…
}
定义一个 Usber 接口让 Phone 和 Camera 结构体实现这个接口
// 接口是一个规范
type Usber interface {
start()
stop()
}
type Phone struct {
Name string
}
func (p Phone) start() {
fmt.Println(p.Name, "启动")
}
func (p Phone) stop() {
fmt.Println(p.Name, "关机")
}
type Camera struct {
}
func (p Camera) start() {
fmt.Println("相机启动")
}
func (p Camera) stop() {
fmt.Println("相机关机")
}
func (p Camera) run() {
fmt.Println("run")
}
func main() {
p := Phone{
Name: "华为手机",
}
var p1 Usber
p1 = p
p1.stop() //华为手机 关机
c := Camera{}
var c1 Usber = c
// c1.start1() //报错
c1.start() //相机启动
c.run() //run
}
Computer 结构体中的 Work 方法必须传入一个 Usb 的接口
package main
import "fmt"
// 接口是一个规范
type Usber interface {
start()
stop()
}
type Computer struct {
}
func (c Computer) work(usb Usber) {
usb.start()
usb.stop()
}
type Phone struct {
Name string
}
func (p Phone) start() {
fmt.Println(p.Name, "启动")
}
func (p Phone) stop() {
fmt.Println(p.Name, "关机")
}
type Camera struct {
}
func (p Camera) start() {
fmt.Println("相机启动")
}
func (p Camera) stop() {
fmt.Println("相机关机")
}
func (p Camera) run() {
fmt.Println("run")
}
func main() {
var computer = Computer{}
var phone = Phone{
Name: "小米",
}
var camera = Camera{}
computer.work(phone)
//小米 启动
//小米 关机
computer.work(camera)
// 相机启动
//相机关机
}
空接口
Golang 中的接口可以不定义任何方法,没有定义任何方法的接口就是空接口。空接口表示 没有任何约束,因此任何类型变量都可以实现空接口。
空接口在实际项目中用的是非常多的,用空接口可以表示任意数据类型。
type A interface{} //空接口 表示没有任何约束 任意的类型都可以实现空接口
func main() {
var a A
var str = "你好golang"
a = str //让字符串实现A这个接口
fmt.Printf("值:%v 类型:%T\n", a, a) //值:你好golang 类型:string
var num = 20
a = num //表示让int类型实现A这个接口
fmt.Printf("值:%v 类型:%T\n", a, a)
var flag = true
a = flag //表示让bool类型实现A这个接口
fmt.Printf("值:%v 类型:%T", a, a)
}
空接口作为函数的参数
使用空接口实现可以接收任意类型的函数参数。
func show(a interface{}) {
fmt.Printf("值:%v 类型:%T\n", a, a)
}
func main() {
show(20) //值:20 类型:int
show("你好golang") //值:你好golang 类型:string
slice := []int{1, 2, 34, 4}
show(slice) //值:[1 2 34 4] 类型:[]int
}
map 的值实现空接口
var m1 = make(map[string]interface{})
m1["username"] = "张三"
m1["age"] = 20
m1["married"] = true
fmt.Println(m1) //map[age:20 married:true username:张三]
切片实现空接口
var s1 = []interface{}{1, 2, "你好", true} //[1 2 你好 true]
fmt.Println(s1) //[1 2 你好 true]
类型断言
一个接口的值(简称接口值)是由一个具体类型和具体类型的值两部分组成的。这两部分分 别称为接口的动态类型和动态值。
如果我们想要判断空接口中值的类型,那么这个时候就可以使用类型断言,其语法格式:x.(T)
- x : 表示类型为 interface{}的变量 •
- T : 表示断言 x 可能是的类型
该语法返回两个参数,第一个参数是 x 转化为 T 类型后的变量,第二个值是一个布尔值,若为 true 则表示断言成功,为 false 则表示断言失败。
var a interface{}
a = "你好golang"
v, ok := a.(string)
if ok {
fmt.Println("a就是一个string类型,值是:", v)
} else {
fmt.Println("断言失败")
}
//a就是一个string类型,值是: 你好golang
func MyPrint1(x interface{}) {
if _, ok := x.(string); ok {
fmt.Println("string类型")
} else if _, ok := x.(int); ok {
fmt.Println("int类型")
} else if _, ok := x.(bool); ok {
fmt.Println("bool类型")
}
}
func MyPrint2(x interface{}) {
switch x.(type) {
case int:
fmt.Println("int类型")
case string:
fmt.Println("string类型")
case bool:
fmt.Println("bool类型")
default:
fmt.Println("传入错误...")
}
}
MyPrint1("你好golang") //string类型
MyPrint1(true) //bool类型
MyPrint2("你好golang") //string类型
MyPrint2(true) //bool类型
结构体值接收者和指针接收者实现接口的区别
值接收者:
如果结构体中的方法是值接收者,那么实例化后的结构体值类型和结构体指针类型都可以赋 值给接口变量
type Usber interface {
start()
stop()
}
//电脑
type Computer struct {
}
func (c Computer) work(usb Usber) {
//要判断usb的类型
if _, ok := usb.(Phone); ok { //类型断言
usb.start()
} else {
usb.stop()
}
}
//手机
type Phone struct {
Name string
}
func (p Phone) start() {
fmt.Println(p.Name, "启动")
}
func (p Phone) stop() {
fmt.Println(p.Name, "关机")
}
//照相机
type Camera struct {
}
func (p Camera) start() {
fmt.Println("相机启动")
}
func (p Camera) stop() {
fmt.Println("相机关机")
}
func main() {
var computer = Computer{}
var phone = Phone{
Name: "小米手机",
}
var camera = Camera{}
computer.work(phone) //小米手机 启动
computer.work(camera) //相机 关机
}
指针接收者
如果结构体中的方法是指针接收者,那么实例化后结构体指针类型都可以赋值给接口变量, 结构体值类型没法赋值给接口变量
type Usber interface {
start()
stop()
}
type Phone struct {
Name string
}
func (p *Phone) start() { //指针接收者
fmt.Println(p.Name, "启动")
}
func (p *Phone) stop() {
fmt.Println(p.Name, "关机")
}
func main() {
/*
错误写法
var phone1 = Phone{
Name: "小米",
}
var p1 Usber = phone1 // Phone does not implement Usber (start method has pointer receiver
p1.start()
*/
var phone1 = &Phone{
Name: "小米",
}
var p1 Usber = phone1
p1.start()
}
定义一个Animal的接口,多个结构体实现这个方法
定义一个Animal的接口,Animal中定义两个方法,分别是SetName和GetName。分别让Dog结构体和Cat结构体实现这个方法
package main
import "fmt"
type Animaler interface {
SetName(string)
GetName() string
}
type Dog struct {
Name string
}
func (d *Dog) SetName(name string) {
d.Name = name
}
func (d Dog) GetName() string {
return d.Name
}
type Cat struct {
Name string
}
func (c *Cat) SetName(name string) {
c.Name = name
}
func (c Cat) GetName() string {
return c.Name
}
func main() {
//Dog实现Animal的接口
d := &Dog{
Name: "小黑",
}
var d1 Animaler = d
fmt.Println(d1.GetName())
d1.SetName("阿奇")
fmt.Println(d1.GetName())
//Cat实现Animal的接口
c := &Cat{
Name: "小花",
}
var c1 Animaler = c
fmt.Println(c1.GetName())
}
一个结构体实现多个接口
type Animaler1 interface {
SetName(string)
}
type Animaler2 interface {
GetName() string
}
type Dog struct {
Name string
}
func (d *Dog) SetName(name string) {
d.Name = name
}
func (d Dog) GetName() string {
return d.Name
}
func main() {
//Dog实现Animal的接口
d := &Dog{
Name: "小黑",
}
var d1 Animaler1 = d //表示让Dog实现Animaler1这个接口
var d2 Animaler2 = d //表示让Dog实现Animaler2这个接口
d1.SetName("小花狗狗")
fmt.Println(d2.GetName()) //小花狗狗
}
接口嵌套
type Ainterface interface {
SetName(string)
}
type Binterface interface {
GetName() string
}
type Animaler interface { //接口的嵌套
Ainterface
Binterface
}
type Dog struct {
Name string
}
func (d *Dog) SetName(name string) {
d.Name = name
}
func (d Dog) GetName() string {
return d.Name
}
func main() {
//Dog实现Animal的接口
d := &Dog{
Name: "小黑",
}
var d1 Animaler = d //表示让Dog实现Animaler这个接口
d1.SetName("小花狗狗")
fmt.Println(d1.GetName()) //小花狗狗
}
反射
反射的引子
- 空接口可以存储任意类型的变量,那我们如何知道这个空接口保存数据的类型是什么? 值是什么呢?
可以使用类型断言
可以使用反射实现,也就是在程序运行时动态的获取一个变量的类型信息和值信息。
-
把结构体序列化成 json 字符串,自定义结构体 Tag 标签的时候就用到了反射
-
后期我们会给大家讲 ORM 框架,这个 ORM 框架就用到了反射技术
reflect.TypeOf()
reflect.TypeOf()获取任意值的类型对象
使用 reflect.TypeOf()函数可以接受任意 interface{}参数,可以获得任意值的类 型对象(reflect.Type),程序通过类型对象可以访问任意值的类型信息。
type myInt int
type Person struct {
Name string
Age int
}
func reflectFn(x interface{}) {
v := reflect.TypeOf(x)
// v.Name() //类型名称 ,种类(Kind)就是指底层的类型
// v.Kind() //种类
fmt.Printf("类型:%v 类型名称:%v 类型种类:%v \n", v, v.Name(), v.Kind())
}
func main() {
a := 10 //int
b := 23.4 //float64
c := true //bool
d := "你好golang" //string
reflectFn(a)
reflectFn(b)
reflectFn(c)
reflectFn(d)
var e myInt = 34
var f = Person{
Name: "张三",
Age: 20,
}
reflectFn(e) //main.myInt
reflectFn(f) //main.Person
var h = 25
reflectFn(&h) //*int 类型名称: 类型种类:ptr
var i = [3]int{1, 2, 3}
reflectFn(i) //[3]int 类型名称: 类型种类:array
var j = []int{11, 22, 33}
reflectFn(j) //[]int 类型名称: 类型种类:slice
}
reflect.ValueOf()
反射获取变量的原始值1
func reflectValue(x interface{}) {
// var num = 10 + x //(mismatched types int and interface {}
// b, _ := x.(int)
// var num = 10 + b
// fmt.Println(num) //23
//反射来实现这个功能
// v := reflect.ValueOf(x)
// fmt.Println(v) //13
// var n = v + 12
// fmt.Println(n) //mismatched types reflect.Value and int
//反射获取变量的原始值
v := reflect.ValueOf(x)
var m = v.Int() + 12
fmt.Println(m) //25
}
func main() {
var a = 13
reflectValue(a)
}
通过反射获取原始值演示 2
func reflectValue(x interface{}) {
v := reflect.ValueOf(x)
// v.Kind() //获取种类
kind := v.Kind()
switch kind {
case reflect.Int64:
fmt.Printf("int类型的原始值%v,计算后的值是%v \n", v.Int(), v.Int()+10)
case reflect.Float32:
fmt.Printf("Float32类型的原始值%v\n", v.Float())
case reflect.Float64:
fmt.Printf("Float64类型的原始值%v\n", v.Float())
case reflect.String:
fmt.Printf("string类型的原始值%v\n", v.String())
default:
fmt.Printf("还没有判断这个类型\n")
}
}
func main() {
var a int64 = 100
var b float32 = 12.3
var c string = "你好golang"
reflectValue(a) //int类型的原始值100,计算后的值是110
reflectValue(b) //Float32类型的原始值12.300000190734863
reflectValue(c) //string类型的原始值你好golang
}
3
func reflectSetValue(x interface{}) {
// *x = 120 //invalid indirect of x (type interface {})
// v, _ := x.(*int)
// *v = 120 // invalid memory address or nil pointer dereferenc
v := reflect.ValueOf(x)
// fmt.Println(v.Kind()) //ptr
// fmt.Println(v.Elem().Kind()) //int64
if v.Elem().Kind() == reflect.Int64 {
v.Elem().SetInt(123)
} else if v.Elem().Kind() == reflect.String {
v.Elem().SetString("你好 go语言")
}
}
func main() {
var a int64 = 100
reflectSetValue(&a)
fmt.Println(a)
var b string = "你好golang"
reflectSetValue(&b) //123
fmt.Println(b) //你好 go语言
}
结构体反射
获取结构体属性,获取执行结构体方法
type Student struct {
Name string `json:"name1" form:"username"`
Age int `json:"age"`
Score int `json:"score"`
}
func (s Student) GetInfo() string {
var str = fmt.Sprintf("姓名:%v 年龄:%v 成绩:%v", s.Name, s.Age, s.Score)
return str
}
func (s *Student) SetInfo(name string, age int, score int) {
s.Name = name
s.Age = age
s.Score = score
}
func (s Student) Print() {
fmt.Println("这是一个打印方法...")
}
//打印字段
func PrintStructField(s interface{}) {
//判断参数是不是结构体类型
t := reflect.TypeOf(s)
v := reflect.ValueOf(s)
if t.Kind() != reflect.Struct && t.Elem().Kind() != reflect.Struct {
fmt.Println("传入的参数不是一个结构体")
return
}
//1、通过类型变量里面的Field可以获取结构体的字段
field0 := t.Field(0)
fmt.Printf("%#v \n", field0) //reflect.StructField{Name:"Name", PkgPath:"", Type:(*reflect.rtype)(0x4adf20), Tag:"json:\"name\"", Offset:0x0, Index:[]int{0}, Anonymous:false}
fmt.Println("字段名称:", field0.Name)
fmt.Println("字段类型:", field0.Type)
fmt.Println("字段Tag:", field0.Tag.Get("json"))
fmt.Println("字段Tag:", field0.Tag.Get("form"))
//2、通过类型变量里面的FieldByName可以获取结构体的字段
fmt.Println("----------------------")
field1, ok := t.FieldByName("Age")
if ok {
fmt.Println("字段名称:", field1.Name)
fmt.Println("字段类型:", field1.Type)
fmt.Println("字段Tag:", field1.Tag.Get("json"))
}
//3、通过类型变量里面的NumField获取到该结构体有几个字段
var fieldCount = t.NumField()
fmt.Println("结构体有", fieldCount, "个属性")
//4、通过值变量获取结构体属性对应的值
fmt.Println(v.FieldByName("Name"))
fmt.Println(v.FieldByName("Age"))
fmt.Println("----------------------")
for i := 0; i < fieldCount; i++ {
fmt.Printf("属性名称:%v 属性值:%v 属性类型:%v 属性Tag:%v\n", t.Field(i).Name, v.Field(i), t.Field(i).Type, t.Field(i).Tag.Get("json"))
}
}
//打印执行方法
func PrintStructFn(s interface{}) {
t := reflect.TypeOf(s)
v := reflect.ValueOf(s)
if t.Kind() != reflect.Struct && t.Elem().Kind() != reflect.Struct {
fmt.Println("传入的参数不是一个结构体")
return
}
//1、通过类型变量里面的Method可以获取结构体的方法
method0 := t.Method(0) //和结构体方法的顺序没有关系,和结构体方法的ASCII有关系
fmt.Println(method0.Name) //GetInfo
fmt.Println(method0.Type) //func(main.Student) string
fmt.Println("--------------------------")
//2、通过类型变量获取这个结构体有多少个方法
method1, ok := t.MethodByName("Print")
if ok {
fmt.Println(method1.Name) //Print
fmt.Println(method1.Type) //func(main.Student)
}
fmt.Println("--------------------------")
//3、通过《值变量》执行方法 (注意需要使用值变量,并且要注意参数) v.Method(0).Call(nil) 或者v.MethodByName("Print").Call(nil)
// v.Method(1).Call(nil)
v.MethodByName("Print").Call(nil)
info1 := v.MethodByName("GetInfo").Call(nil)
fmt.Println(info1)
//4、执行方法传入参数 (注意需要使用《值变量》,并且要注意参数,接收的参数是[]reflect.Value的切片)
var params []reflect.Value
params = append(params, reflect.ValueOf("李四"))
params = append(params, reflect.ValueOf(23))
params = append(params, reflect.ValueOf(99))
v.MethodByName("SetInfo").Call(params) //执行方法传入参数
info2 := v.MethodByName("GetInfo").Call(nil)
fmt.Println(info2)
// 5、获取方法数量
fmt.Println("方法数量:", t.NumMethod())
}
func main() {
stu1 := Student{
Name: "小明",
Age: 15,
Score: 98,
}
// PrintStructField(stu1)
PrintStructFn(&stu1)
fmt.Printf("%#v\n", stu1)
}
修改结构体方法
type Student struct {
Name string `json:"name"`
Age int `json:"age"`
Score int `json:"score"`
}
func (s Student) GetInfo() string {
var str = fmt.Sprintf("姓名:%v 年龄:%v 成绩:%v", s.Name, s.Age, s.Score)
return str
}
// //反射修改结构体属性
func reflectChangeStruct(s interface{}) {
t := reflect.TypeOf(s)
v := reflect.ValueOf(s)
if t.Kind() != reflect.Ptr {
fmt.Println("传入的不是结构体指针类型")
return
} else if t.Elem().Kind() != reflect.Struct {
fmt.Println("传入的不是结构体指针类型")
return
}
//修改结构体属性的值
name := v.Elem().FieldByName("Name")
name.SetString("小李")
age := v.Elem().FieldByName("Age")
age.SetInt(22)
}
func main() {
stu1 := Student{
Name: "小明",
Age: 15,
Score: 98,
}
// PrintStructField(stu1)
reflectChangeStruct(&stu1)
fmt.Printf("%#v\n", stu1) //main.Student{Name:"小李", Age:22, Score:98}
}