go第三章--函数,结构体,接口,反射

277 阅读19分钟

函数

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)

  1. x : 表示类型为 interface{}的变量 •
  2. 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()) //小花狗狗

}

反射

反射的引子

  1. 空接口可以存储任意类型的变量,那我们如何知道这个空接口保存数据的类型是什么? 值是什么呢?

可以使用类型断言

可以使用反射实现,也就是在程序运行时动态的获取一个变量的类型信息和值信息。

  1. 把结构体序列化成 json 字符串,自定义结构体 Tag 标签的时候就用到了反射

  2. 后期我们会给大家讲 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}

}