5 map、结构体、方法、面向对象【Go语言教程】

有时候,不是因为你没有能力,也不是因为你缺少勇气,只是因为你付出的努力还太少,所以,成功便不会走向你。而你所需要做的,就是坚定你的梦想,你的目标,你的未来,然后以不达目的誓不罢休的那股劲,去付出你的努力,成功就会慢慢向你靠近。

导读:本篇文章讲解 5 map、结构体、方法、面向对象【Go语言教程】,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

5 map、结构体、方法、面向对象【Go语言教程】

1 map

1.1 概念及声明

①概念

基本语法:var map 变量名 map[keytype]valuetype

  • key 可以是什么类型?
    golang 中的 map,的 key 可以是很多种类型,比如 bool, 数字,string, 指针, channel , 还可以是只包含前面几个类型的 接口, 结构体, 数组
    通常 key 为 int 、string
    注意: slice, map 还有 function 不可以,因为这几个没法用 == 来判断
  • valuetype 可以是什么类型?
    valuetype 的类型和 key 基本一样,这里我就不再赘述了通常为: 数字(整数,浮点数),string,map,struct

②声明方式

  1. var a map[string]string
  2. var a map[string]int
  3. var a map[int]string
  4. var a map[string]map[string]string
    注意:声明是不会分配内存的,初始化需要 make ,分配内存后才能赋值和使用
func main(){
	//map的声明和注意事项
	var a map[string]string
	//在使用map之前,需要先make(给map分配内存空间)
	a = make(map[string]string, 10)
	a["no1"] = "curry"
	a["no2"] = "徐杰"
	a["no3"] = "John"
	a["no4"] = "欧文"
	a["no3"] = "Bob"
	fmt.Println(a)
}

在这里插入图片描述

对上面的代码进行说明:

  1. map 在使用前一定要 make
  2. map 的 key 是不能重复,如果重复了,则以最后这个 key-value 为准
  3. map 的 value 是可以相同的.
  4. map 的 key-value 是无序
  5. make 内置函数数目

在这里插入图片描述
map的三种初始化方式:

package main
import (
	"fmt"
)

func main(){
	//map的初始化方式
	//1. 先声明后make
	var a map[string]string
	//在使用map前,需要先make(给map分配数据空间)
	a = make(map[string]string, 10)
	a["no1"] = "天暗星"
	a["no2"] = "天捷星"
	a["no3"] = "天速星"
	fmt.Println(a)
	//2. 声明时直接make
	cities := make(map[string]string)
	cities["no1"] = "newYork"
	cities["no2"] = "taibei"
	cities["no3"] = "beijing"
	fmt.Println(cities)
	//3. 声明时,直接初始化并赋值
	heros := map[string]string{
		"hero1": "宋江",
		"hero2": "赵云",
		"hero3": "岳飞",
	}
	heros["hero3"] = "林冲"
	fmt.Println("heros=", heros)
	// map[no1:天暗星 no2:天捷星 no3:天速星]
	// map[no1:newYork no2:taibei no3:beijing]     
	// heros= map[hero1:宋江 hero2:赵云 hero3:林冲]	
}

1.2 map的crud

①map的增加和更新

map[“key”] = value //如果 key 还没有,就是增加,如果 key 存在就是修改。

func main(){
	a := make(map[string]string)
	//map中没有存在key1,因此直接新增
	a["key1"] = "jack"
	//map中已经存在了key1,因此直接更新
	a["key1"] = "jackson"
	//map[key1:jackson]
	fmt.Println(a)
}

②map的删除

delete(map,“key”) ,delete 是一个内置函数,如果 key 存在,就删除该 key-value,如果 key 不存在, 不操作,但是也不会报错
在这里插入图片描述
在这里插入图片描述

func main(){
	a := make(map[string]string)
	a["key1"] = "jack"
	//删除key1
	delete(a, "key1")
	//map[]
	fmt.Println(a)
}
  • 注意:如果我们要删除 map 的所有 key ,没有一个专门的方法一次删除,可以遍历一下 key, 逐个删除或者 map = make(…),make 一个新的,让原来的成为垃圾,被 gc 回收
    在这里插入图片描述
    ③map的查找
    在这里插入图片描述

对上面代码的说明:
说明:如果 heroes 这个 map 中存在 “no1” , 那么 findRes 就会返回 true,否则返回 false

func main(){
	a := make(map[string]string)
	a["key1"] = "jack"
	a["key2"] = "tom"
	val, ok := a["key3"]
	if ok {
		fmt.Println("值为:", val)
	}else {
		fmt.Println("没找到")
	}
}

1.3 map的遍历与切片

①map的遍历

  • 案例演示相对复杂的 map 遍历:该 map 的 value 又是一个 map
    说明:map 的遍历使用 for-range 的结构遍历
package main
import (
	"fmt"
)

func main(){
	//map通过for-range方式来进行遍历
	//1. 遍历普通map
	cities := make(map[string]string)
	cities["no1"] = "北京"
	cities["no2"] = "天津"
	cities["no3"] = "上海"
	for k, v := range cities {
		fmt.Printf("k=%v v=%v\n", k, v)
	}
	//2. 遍历双重map【map的value又是一个map】
	studentMap := make(map[string]map[string]string)
	//map使用之前需要make make(map[string]string, 3)
	studentMap["stu01"] = make(map[string]string, 3)
	studentMap["stu01"]["name"] = "jack"
	studentMap["stu01"]["sex"] = "男"
	studentMap["stu01"]["address"] = "北京胡同"

	studentMap["stu02"] = make(map[string]string)
	studentMap["stu02"]["name"] = "lucy"
	studentMap["stu02"]["sex"] = "女"
	studentMap["stu02"]["address"] = "上海外滩"

	for k1, v1 := range studentMap {
		fmt.Println("k1=", k1)
		for k2, v2 := range v1 {
			fmt.Printf("\t k2=%v v2=%v\n", k2, v2)
		}
		fmt.Println()
	}
}

在这里插入图片描述
在这里插入图片描述
②map切片

切片的数据类型如果是 map,则我们称为 slice of map,map 切片,这样使用则 map 个数就可以动态变化了。

案例:

要求:使用一个 map 来记录 monster 的信息 name 和 age, 也就是说一个 monster 对应一个 map,并且妖怪的个数可以动态的增加=>map 切片

package main
import (
	"fmt"
)

func main(){
	//1. 声明一个map切片
	var monsters []map[string]string
	monsters = make([]map[string]string, 2) //准备放入2个妖怪
	monsters[0] = make(map[string]string, 2)
	monsters[0]["name"] = "牛魔王"
	monsters[0]["age"] = "500"

	monsters[1] = make(map[string]string, 2)
	monsters[1]["name"] = "狐狸精"
	monsters[1]["age"] = "300"
	//下面写法越界,因为只开辟了两块空间
	//panic: runtime error: index out of range [2] with length 2 
	// monsters[2] = make(map[string]string, 2)
	//这里我们可以使用切片的append函数,动态的增加monster
	//①定义monster信息
	newMonster := map[string]string{
		"name" : "新妖怪~火云邪神",
		"age" : "200",
	}
	monsters = append(monsters, newMonster)
	// map[age:300 name:狐狸精] map[age:200 name:新妖怪~火云邪神]]
	fmt.Println(monsters)
}

1.3 map的排序及使用细节

①map的排序

  1. golang 中没有一个专门的方法针对 map 的 key 进行排序
  2. golang 中的 map 默认是无序的,注意也不是按照添加的顺序存放的,你每次遍历,得到的输出可能不一样.
  3. golang 中 map 的排序,是先将 key 进行排序,然后根据 key 值遍历输出即可
package main
import (
	"fmt"
	"sort"
)

func main(){
	//map的排序
	map1 := make(map[int]int, 10)
	map1[10] = 100
	map1[1] = 13
	map1[4] = 56
	map1[0] = 90

	fmt.Println(map1)

	//map的排序:
	//1. 先将map的key放入到切片中
	//2. 对切片进行排序
	//3. 遍历切片,然后按照key来输出map的值
	var keys []int
	for k, _ := range map1 {
		keys = append(keys, k)
	}
	//排序
	sort.Ints(keys)
	//根据排序后的切片输出map中key对应的value
	for _, k := range keys {
		fmt.Printf("map1[%v]=%v \n", k, map1[k])
	}
}

在这里插入图片描述
②使用细节

  1. map 是引用类型,遵守引用类型传递的机制,在一个函数接收 map,修改后,会直接修改原来的 map
    在这里插入图片描述
  1. map 的容量达到后,再想 map 增加元素,会自动扩容,并不会发生 panic,也就是说 map 能动态的增长 键值对(key-value)
  2. map 的 value 也经常使用 struct 类型,更适合管理复杂的数据(比前面 value 是一个 map 更好),比如 value 为 Student 结构体
    在这里插入图片描述

2 结构体(面向对象特性)

2.1 概念

Golang中面向对象的特点:

  1. Golang 也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说 Golang 支持面向对象编程特性是比较准确的。

  2. Golang 没有类(class),Go 语言的结构体(struct)和其它编程语言的类(class)有同等的地位,你可以理解 Golang 是基于 struct 来实现 OOP 特性的。

  3. Golang 面向对象编程非常简洁,去掉了传统 OOP 语言的继承、方法重载、构造函数和析构函数、隐藏的 this 指针等等

  4. Golang 仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它 OOP 语言不一样,比如继承 :Golang 没有 extends 关键字,继承是通过匿名字段来实现。

  5. Golang 面向对象(OOP)很优雅,OOP 本身就是语言类型系统(type system)的一部分,通过接口(interface)关联,耦合性低,也非常灵活。后面同学们会充分体会到这个特点。也就是说在Golang 中面向接口编程是非常重要的特性。

结构体变量(实例)在内存的布局(重要!)
在这里插入图片描述

2.2 声明、使用及注意细节

①声明

type 结构体名称 struct { 
	field1 type
	field2 type

}
  1. 从概念或叫法上看: 结构体字段 = 属性 = field (即授课中,统一叫字段)
  2. 字段是结构体的一个组成部分,一般是基本数据类型、数组,也可是引用类型。比如我们前面定义猫结构体 的 Name string 就是属性

②使用

package main
import (
	"fmt"
	_ "sort"
)

//首字母大写表明公有
type Person struct {
	Name string
	Age int
	slice []int //切片
	map1 map[string]string //map
}

func main(){
	//1. 直接声明
	var person Person 
	person.slice = make([]int, 5)
	person.map1 = make(map[string]string)
	fmt.Println("person=", person)

	//2. 直接声明并初始化 {}
	var person2 Person = Person{"jack", 20, make([]int, 5), make(map[string]string)}
	fmt.Println("person2=", person2)
	// person3 := Person{"jucy", 0, make([]int, 5), make(map[string]string)}
	// fmt.Println("person3=", person3)

	//3. &
	var p3 *Person = new(Person)
	//因为p3是一个指针, 因此:
	//(*)p3.Name = "smith" 也可以这样写 p3.Name = "smith"
	//go的设计者为了程序员使用方便,所以在底层帮我们做了封装,底层会对 p3.Name = "smith"进行处理,会变为(*p3).Name
	(*p3).Name = "smith"
	p3.Name = "john"
	fmt.Println("person3=", p3)

	//4. {}
	var p4 *Person = &Person{}
	(*p4).Name = "scott"
	p4.Name = "scott~~"
	fmt.Println("person4=",*p4)
}

在这里插入图片描述

③注意细节

  1. 字段声明语法同变量,示例:字段名 字段类型
  2. 字段的类型可以为:基本类型、数组或引用类型
  3. 在创建一个结构体变量后,如果没有给字段赋值,都对应一个零值(默认值),规则同前面讲的一样:
    布尔类型是 false ,数值是 0 ,字符串是 “”。
    数组类型的默认值和它的元素类型相关,比如 score [3]int 则为[0, 0, 0]
    指针,slice,和 map 的零值都是 nil ,即还没有分配空间。
    在这里插入图片描述
  1. 不同结构体变量的字段是独立,互不影响,一个结构体变量字段的更改,不影响另外一个, 结构体是值类型。

注意:

  1. 结构体的所有字段在内存中是连续的
  2. 结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)
  3. 结构体进行 type 重新定义(相当于取别名),Golang 认为是新的数据类型,但是相互间可以强转
  4. struct 的每个字段上,可以写上一个 tag, 该 tag 可以通过反射机制获取,常见的使用场景就是序列化和反序列化。

序列化使用场景:
在这里插入图片描述

package main
import (
	"fmt"
	"encoding/json"
)

//前后端传输过程中,前端更希望字段名为小写,但是go语言如果为了让其他包能访问到字段只能
//首字母大写,因此就需要struct的tag来完成
type Monster struct {
	Name string `json:"name"` //`json:"name"`就是strcut tag
	Age int `json:"age"`
	Skill string `json:"skill"`
}


func main(){
	monster := Monster{"牛魔王", 500, "芭蕉扇~"}
	//将monster变量序列化为json格式字符串
	//json.Marshal 函数中使用反射
	jsonStr, err := json.Marshal(monster)
	if err != nil {
		fmt.Println("json处理错误 ", err)
	} 
	//jsonStr {"name":"牛魔王","age":500,"skill":"芭蕉扇~"}
	fmt.Println("jsonStr", string(jsonStr))
}

3 方法

3.1 介绍

在某些情况下,我们要需要声明(定义)方法。比如 Person 结构体:除了有一些字段外( 年龄,姓名…),Person 结构体还有一些行为比如:可以说话、跑步…,通过学习,还可以做算术题。这时就要用方法才能完成。

  • Golang 中的方法是作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型, 都可以有方法,而不仅仅是 struct。

3.2 方法的声明和调用

package main
import (
	"fmt"
)

type Person struct {
	Name string
}

//给Person绑定一个方法[绑定之后只有Person才能使用]
func (p Person) test(){
	fmt.Println("test() name=", p.Name)
}

func main(){
	var p Person
	p.Name = "tom"
	p.test()//调用方法
}
  1. test 方法和 Person 类型绑定
  2. test 方法只能通过 Person 类型的变量来调用,而不能直接调用,也不能使用其它类型变量来调
  3. func (p Person) test() {}… p 表示哪个 Person 变量调用,这个 p 就是它的副本, 这点和函数传参非常相似。
  4. p 这个名字,有程序员指定,不是固定, 比如修改成 person 也是可以

3.3 方法的调用和传参机制(重要)

方法的调用和传参机制和函数基本一样,不一样的地方是方法调用时,会将调用方法的变量,当做实参也传递给方法。下面我们举例说明。

例如:getSum的方法过程
在这里插入图片描述

说明:

  1. 在通过一个变量去调用方法时,其调用机制和函数一样
  2. 不一样的地方时,变量调用方法时,该变量本身也会作为一个参数传递到方法(如果变量是值类型,则进行值拷贝,如果变量是引用类型,则进行地址拷贝)

3.4 注意事项

①声明和定义

func (recevier type) methodName(参数列表) (返回值列表){
方法体
return 返回值
}

  1. 参数列表:表示方法输入
  2. recevier type : 表示这个方法和 type 这个类型进行绑定,或者说该方法作用于 type 类型
  3. receiver type : type 可以是结构体,也可以其它的自定义类型
  4. receiver : 就是 type 类型的一个变量(实例),比如 :Person 结构体 的一个变量(实例)
  5. 返回值列表:表示返回的值,可以多个
  6. 方法主体:表示为了实现某一功能代码块
  7. return 语句不是必须的。

②注意事项和细节

  1. 结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式
  2. 如程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理
  3. Golang 中的方法作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型, 都可以有方法,而不仅仅是 struct, 比如 int , float32 等都可以有方法
  4. 方法的访问范围控制的规则,和函数一样。方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其它包访问。
  5. 如果一个类型实现了 String()这个方法,那么 fmt.Println 默认会调用这个变量的 String()进行输出【类比java中重写了toString方法】

3.5 方法和函数的区别

  1. 调用方式不一样
    函数的调用方式: 函数名(实参列表)
    方法的调用方式: 变量.方法名(实参列表)
  2. 对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然
  3. 对于方法(如 struct 的方法),接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以
package main

import (
	"fmt"
)

type Person struct {
	Name string
}

//对于方法(如:struct的方法)
//接收者为值类型时,可以直接使用指针类型的变量调用方法,反过来也同样可以

//1. 和结构体类型绑定
func (p Person) test03(){
	p.Name = "jack"
	fmt.Println("test03()=", p.Name)
}

//2. 和指针类型绑定
func (p *Person) test04(){
	p.Name = "mary"
	fmt.Println("test04()=", p.Name)
}



func main(){
	var p Person
	p.Name = "tom"
	p.test03()
	fmt.Println("main() p.Name=", p.Name) //tom

	(&p).test03() //从形式上看是传入地址,但是本质上仍然是值拷贝【因为test03方法是和结构体绑定的】
	fmt.Println("main() p.Name=", p.Name) //tom

	(&p).test04()
	fmt.Println("main() p.Name=", p.Name) //mary
	p.test04() //等价(&p).test04(),从形式上是传入值类型,但是本质仍然是地址拷贝
	//【调用方不决定,决定权在调用的方法和什么绑定】

}

总结:

  1. 不管调用形式如何,真正决定是值拷贝还是地址拷贝,看这个方法是和哪个类型绑定.
  2. 如果是和值类型,比如 (p Person) , 则是值拷贝, 如果和指针类型,比如是 (p *Person) 则是地址拷贝。

3.6 工厂模式(类比get、set)

在java中为了访问私有的属性,通常会提供对应的get、set方法,但是Go中并没有;因此我们可以使用工厂模式来达到类似的效果

  • Golang 的结构体没有构造函数,通常可以使用工厂模式来解决这个问题。
一个结构体的声明是这样的: 
package model
type Student struct { 
	Name string...
}

因为这里的Student 的首字母S 是大写的,如果我们想在其它包创建 Student 的实例(比如main 包), 引入 model 包后,就可以直接创建 Student 结构体的变量(实例)。但是问题来了,如果首字母是小写的, 比如 是 type student struct {…} 就不不行了,怎么办—> 工厂模式来解决.

  1. model包中定义一个结构体student

main.go:

package model

//定义一个私有的结构体
//Name字段是共有的,age字段是私有的
type student struct {
	Name string
	age int
}

//工厂模式:类比构造函数
//返回Student指针类型
func NewStudent(name string, age int) *student{
	return &student{
		Name: name,
		age: age,
	}
}

//工厂模式定义student对应的方法:类比get、set方法
func (stu *student) GetAge() int{
	return (*stu).age
}

func (stu *student) SetAge (age int) {
	(*stu).age = age
}
  1. 在main包下使用student

hello.go

package main

import (
	"fmt"
	"go_code/project01/main/model"
)

func main(){
	//创建一个student
	var stu = model.NewStudent("tom", 20)
	stu.SetAge(17)
	//因为Name在model中是首字母大写表明是公有的,因此可以直接访问;而age需要通过GetAge函数实现
	//tom 17
	fmt.Println(stu.Name, stu.GetAge())
}

在这里插入图片描述

4 面向对象

4.1 面向对象练习题(封装)

  1. 创建程序,在 model 包中定义 Account 结构体:在 main 函数中体会 Golang 的封装性。
  2. Account 结构体要求具有字段:账号(长度在 6-10 之间)、余额(必须>20)、密码(必须是六
  3. 通过 SetXxx 的方法给 Account 的字段赋值。(同学们自己完成
  4. 在 main 函数中测试
  1. 定义account结构体
package model

import (
	"fmt"
)

type account struct {
	accountNo string //账户
	Balance float64	//余额
	Password string
}

//使用工厂方法完成构造
func NewAccount(accountNo string, Balance float64, Password string) *account {
	if len(accountNo) < 6 || len(accountNo) > 10 {
		fmt.Println("账号必须在6-10之间...")
		return nil
	}
	if Balance < 20 {
		fmt.Println("余额不能少于20...")
		return nil
	}
	if len(Password) != 6 {
		fmt.Println("密码必须是6位..")
		return nil
	}
	account := account{
		accountNo: accountNo,
		Balance: Balance,
		Password: Password,
	}
	fmt.Println("创建账户成功~~~")
	return &account
}

//定义get方法[与结构体绑定]
func (a account) GetAccountNo() string{
	return a.accountNo
}

//set方法[与指针绑定]
func (a *account) SetAccountNo(accountNo string){
	(*a).accountNo = accountNo
}
  1. 使用
package main

import (
	"fmt"
	"go_code/project01/main/model"
)

func main(){
	//创建一个账户
	var account = model.NewAccount("4131341324", 35.5, "348291")
	fmt.Println("账户No=", account.GetAccountNo())
	account.SetAccountNo("6666666666")
	fmt.Println("新的账户No=", account.GetAccountNo())
}

结果:
在这里插入图片描述

4.2 面向对象(继承)

①概念

在这里插入图片描述

继承的优点:

  1. 代码的复用性提高了
  2. 代码的扩展性和维护性提高了
type Goods struct { Name string Price int
}
type Book struct {
//这里就是嵌套匿名结构体 Goods Writer string
	Goods	
}

②使用及注意点

继承的深入讨论:

  1. 结构体可以使用嵌套匿名结构体所有的字段和方法,即:首字母大写或者小写的字段、方法, 都可以使用。【举例说明】
package main

import (
	"fmt"
	_ "go_code/project01/main/model"
)

type A struct {
	Name string
	age int
}

//公有
func (a *A) SayOk(){
	fmt.Println("A SayOk ", a.Name)
}

//私有
func (a *A) hello(){
	fmt.Println("A hello", a.Name)
}


type B struct {
	//B继承了A
	A
}

func main(){
	var b B
	b.A.Name = "tom"
	//b.Name = "jack" //两种写法均可
	b.age = 12
	b.SayOk()
	b.hello()
}
  1. 匿名结构体字段访问可以简化
	b.A.Name = "tom"
	//b.Name = "jack" //两种写法均可

对上面的代码小结
(1) 当我们直接通过 b 访问字段或方法时,其执行流程如下比如 b.Name
(2) 编译器会先看 b 对应的类型有没有 Name, 如果有,则直接调用 B 类型的 Name 字段
(3) 如果没有就去看 B 中嵌入的匿名结构体 A 有没有声明 Name 字段,如果有就调用,如果没有继续查找…如果都找不到就报错.

  1. 当结构体和匿名结构体有相同的字段或者方法时,编译器采用就近访问原则访问,如希望访问匿名结构体的字段和方法,可以通过匿名结构体名来区分【举例说明】
    在这里插入图片描述
  2. 结构体嵌入两个(或多个)匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须明确指定匿名结构体名字,否则编译报错。【举例说明】
    在这里插入图片描述
  3. 如果一个 struct 嵌套了一个有名结构体,这种模式就是组合,如果是组合关系,那么在访问组合的结构体的字段或方法时,必须带上结构体的名字
    在这里插入图片描述
    在这里插入图片描述
  4. 嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值
package main

import (
	"fmt"
	_ "go_code/project01/main/model"
)

type Goods struct {
	Name string
	Price float64
}

type Brand struct {
	Name string
	Address string
}

type TV struct {
	Goods
	Brand
}

type TV2 struct {
	*Goods
	*Brand
}


func main(){
	//嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值
	tv := TV{Goods{"电视机", 5000.99}, Brand{"海尔", "山东"}}
	tv2 := TV{
		Goods{
			Price: 6999.99,
			Name: "电视机002",
		},
		Brand{
			Name: "夏普",
			Address: "北京",
		},
	}
	fmt.Println("tv=", tv)
	fmt.Println("tv2=", tv2)

	tv3 := TV2{
		&Goods{
			Name: "电视机003",
			Price: 79999.99,
		},
		&Brand{
			Name: "长虹",
			Address: "四川",
		},
	}
	fmt.Println("tv3=", *(tv3.Goods), *(tv3.Brand))
}

在这里插入图片描述7. 结构体的匿名字段是基本数据类型,如果需要有多个 int 的字段,则必须给 int 字段指定名字

package main

import (
	"fmt"
	_ "go_code/project01/main/model"
)

type Monster struct {
	Name string
	Age int
}

type E struct {
	Monster
	int //匿名字段的基本数据类型
	n int
}


func main(){
	//匿名字段是基本数据类型的使用
	var e E
	e.Name = "狐狸精"
	e.Age = 300
	e.int = 20
	e.n = -50
	//e= {{狐狸精 300} 20 -50}
	fmt.Println("e=",e)

}

③多重继承

如一个 struct 嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体的字段和方法,从而实现了多重继承。

在这里插入图片描述

多重继承细节说明

  1. 如嵌入的匿名结构体有相同的字段名或者方法名,则在访问时,需要通过匿名结构体类型名来区分。【案例演示】
    在这里插入图片描述

  2. 为了保证代码的简洁性,建议大家尽量不使用多重继承

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/148510.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!