结构体嵌套
golang
中没有类,他通过结构体来实现其他编程语言中类的相关功能。
具名结构体
基本语法
基本语法
golang
的结构体嵌套特别简单。
type 结构体类型1 struct{
字段 类型1
字段 类型2
}
//这样就实现了结构体的嵌套
type 结构体类型2 struct{
字段 类型1
字段 类型2
字段 结构体类型1
}
举例
package struct_knowledge
import "fmt"
type Worker struct{
salary int
profession string
}
//具名嵌套结构体
type Human struct{
Name string
Age int
Work Worker
}
func (w Worker) GetSalary(){
fmt.Printf("工资为%d\n",w.salary)
}
//访问具名结构体
func VisitNestingStruct(){
var human Human
//访问字段
/*
报错:human.salary undefined(type Human has no filed or method GetSalary)
*/
//human.salary = 25
//通过字段连锁访问
human.Work.salary = 20
fmt.Printf("结构体的实例为%v\n",human)
//通过字面量访问
human = Human{
Age: 20,
Name: "张三",
Work: Worker{
salary: 1000,
profession: "no work",
},
}
fmt.Printf("结构体的实例为%v\n",human)
//通过位置赋值
human = Human{
"lisa",
45,
Worker{
salary: 10000,
profession: "waiter",
},
}
fmt.Printf("结构体的实例为%v\n",human)
// 调用方法
/*
报错:human.GetSalary undefined(type Human has no filed or method GetSalary)
*/
// human.GetSalary()
human.Work.GetSalary()
}
结果
结构体的实例为struct_knowledge.Human{Name:"", Age:0, Work:struct_knowledge.Worker{salary:20, profession:""}}
结构体的实例为struct_knowledge.Human{Name:"张三", Age:20, Work:struct_knowledge.Worker{salary:1000, profession:"no work"}}
结构体的实例为struct_knowledge.Human{Name:"lisa", Age:45, Work:struct_knowledge.Worker{salary:10000, profession:"waiter"}}
工资为10000
访问规则
具名嵌套结构体的访问只能通过
嵌套结构体字段名.字段
嵌套结构体字段名.方法()
不能通过最外层结构体直接访问。
举例
type Worker struct{
salary int
profession string
}
//具名嵌套结构体
type Human struct{
Name string
Age int
Work Worker
}
Human实例不能直接访问Worker实例的字段或方法,需要通过
Human实例.Work.字段
Human实例.Work.方法
匿名结构体
基本语法
就是不写用字段接收嵌套结构体而是直接写类型名,例如
type 结构体类型A struct{
字段1 类型1
字段2 类型2
...
}
type 结构体类型B struct{
字段1 类型1
结构体类型A
}
举例
package struct_knowledge
import "fmt"
type StructA struct{
name string
age int
}
type StructB struct{
price int
StructA
}
func VisitStructB(){
var st StructB
fmt.Printf("嵌套结构体的值为%#v\n",st)
}
结果
嵌套结构体的值为struct_knowledge.StructB{price:0, StructA:struct_knowledge.StructA{name:"", age:0}}
访问规则
名称访问
匿名结构体如果我们将他按照匿名字段理解,那么他就可以解析成如下形式:
type 结构体类型A struct{
字段1 类型1
字段2 类型2
...
}
type 结构体类型B struct{
字段1 类型1
结构体类型A
}
//按照匿名字段理解
type 结构体类型B struct{
字段1 类型1
结构体A 结构体类型A
}
所以我们仍然可以通过如下形式修改和访问:
结构体B实例.结构体A.字段
结构体B实例.结构体A.方法()
举例
package struct_knowledge
import "fmt"
type StructA struct{
name string
age int
}
type StructB struct{
price int
StructA
}
/*
我们可以按照访问具名结构体的方法访问
匿名结构体的名字就是 类型同名
*/
func VisitStructByName(){
var st StructB
//连锁访问
st.StructA.age = 25
fmt.Printf("结构体的实例为%#v\n",st)
//字面量访问
st = StructB{
price: 1000,
StructA: StructA{
age:25,
name:"lisi",
},
}
fmt.Printf("结构体的实例为%#v\n",st)
//位置访问
st = StructB{
1000,
StructA{
age:30,
name:"sam",
},
}
fmt.Printf("结构体的实例为%#v\n",st)
}
结果
结构体的实例为struct_knowledge.StructB{price:0, StructA:struct_knowledge.StructA{name:"", age:25}}
结构体的实例为struct_knowledge.StructB{price:1000, StructA:struct_knowledge.StructA{name:"lisi", age:25}}
结构体的实例为struct_knowledge.StructB{price:1000, StructA:struct_knowledge.StructA{name:"sam", age:30}}
直接访问
匿名结构体与具名结构体最大的不同就是:
我们可以直接通过外层结构体访问匿名结构体的字段或方法(前提没有产生同名冲突)。
举例
package struct_knowledge
import "fmt"
type StructA struct{
name string
age int
}
type StructB struct{
price int
StructA
}
func (c StructA) GetAge(){
fmt.Printf("StructA的年龄为%v\n",c.age)
}
func VisitStructByNoName(){
var st StructB
//在不出现名字冲突的情况下,外层结构体可以直接访问匿名结构体的字段和方法
st.age = 25
st.GetAge()
fmt.Printf("结构体的实例为%#v\n",st)
//但仅限于这种成员访问,字面量和位置就不适用
/*
报错:
unknow field age in struct literal of type StructB
unknow field name in struct literal of type StructB
*/
// st = StructB{
// price: 1000,
// age:30,
// name:"lisa",
// }
}
结果
StructA的年龄为25
结构体的实例为struct_knowledge.StructB{price:0, StructA:struct_knowledge.StructA{name:"", age:25}}
字段冲突
这种情况主要出现在匿名结构体中,因为同一结构体中字段唯一,所以结构体中不能出现同名字段。
但是匿名结构体嵌套后,由于他可以匿名访问,所以就产生了字段和方法冲突。
这也是匿名结构体允许具名访问的原因。
解决方案
当出现字段冲突时,访问匿名结构体的同名字段或方法需要采用具名访问。
举例
package struct_knowledge
import "fmt"
type StructOne struct{
name string
age int
price int
}
type StructTwo struct{
name string
age int
}
type StructThree struct{
name string
StructOne
StructTwo
}
func VisitStructThree(){
var st StructThree
fmt.Printf("结构体的实例为%#v\n",st)
//访问不同名字段,可以直接匿名访问
st.price = 20
/*
但是访问同名字段或方法,就需要指明结构体
如果最外层结构体有就访问的是最外层
*/
st.name = "lisa"
/*
如果访问同名字段或方法,
但最外层结构体也没有就会报错
ambiguous selector st.age
*/
// st.age = 25
}
结果
结构体的实例为struct_knowledge.StructThree{name:"", StructOne:struct_knowledge.StructOne{name:"", age:0, price:0}, StructTwo:struct_knowledge.StructTwo{name:"", age:0}}
结构体的实例为struct_knowledge.StructThree{name:"lisa", StructOne:struct_knowledge.StructOne{name:"", age:0, price:20}, StructTwo:struct_knowledge.StructTwo{name:"", age:0}}
冲突字段访问规则
1.如果要访问的同名字段或者方法顶层有,直接访问则采用顶层的字段或方法。
2.如果要访问的同名字段或者方法顶层没有,直接访问就会报错。
# ambiguous的中文含义:模糊不清的,模棱两可的
ambiguous selector st.xxx
解决字段冲突问题
要访问嵌套结构体的同名字段或者方法,可以采用具名访问法,例如
外层结构体实例.匿名结构体类型.字段
外层结构体实例.匿名结构体类型.方法()