Contents
5.5. 类型内嵌和结构体内嵌¶
5.5.1. 1.匿名字段¶
通过匿名字段,可获得和继承类似的复用能力。依据编译器查找次序,只需在外层定义同名方法,就可以实现 “override”。
简单来说就是实现类似Java中的方法的重载。
package main
import "fmt"
type User struct {
id int
name string
}
type Manager struct {
User
title string
}
func (self *User) ToString() string {
return fmt.Sprintf("User: %p, %v", self, self)
}
func (self *Manager) ToString() string {
return fmt.Sprintf("Manager: %p, %v", self, self)
}
func main() {
m := Manager{User{1, "Tom"}, "Administrator"}
fmt.Println(m.ToString()) //Manager: 0xc000056330, &{{1 Tom} Administrator}
fmt.Println(m.User.ToString()) //User: 0xc000056330, &{1 Tom}
}
结构体允许其成员字段在声明时没有字段名而只有类型,这种形式的字段被称为类型内嵌或匿名字段。
package main
import "fmt"
// 创建结构体
type Data struct {
int
float32
bool
}
// 实例化结构体并赋初值
func main() {
ins :=&Data{
int: 10,
float32: 3.14,
bool: true,
}
fmt.Println(ins.int)
fmt.Println(ins.float32)
fmt.Println(ins.bool)
}
5.5.2. 2.声明结构体内嵌¶
package main
import "fmt"
// 基础颜色
type BasicColor struct {
// 颜色
R, G, B float32
}
// 完整颜色
type Color struct {
// 将基本颜色作为成员
Basic BasicColor
// 透明度
Alpha float32
}
func main() {
// 实例化Color结构体
var c Color
// 设置基本颜色分量
c.Basic.R = 1
c.Basic.G = 2
c.Basic.B = 0
// 设置透明度
c.Alpha = 1
// 显示整个结构体的内容
fmt.Printf("%+v", c) //{Basic:{R:1 G:2 B:0} Alpha:1}
}
使用Go语言的结构体内嵌写法重新调整代码如下:
package main
import "fmt"
// 基础颜色
type BasicColor struct {
// 颜色
R, G, B float32
}
// 完整颜色
type Color struct {
// 将基本颜色作为成员
BasicColor
// 透明度
Alpha float32
}
func main() {
// 实例化Color结构体
var c Color
// 设置基本颜色分量
c.R = 1
c.B = 2
c.G = 0
// 设置透明度
c.Alpha = 1
// 显示整个结构体的内容
fmt.Printf("%+v", c) //{Basic:{R:1 G:2 B:0} Alpha:1}
}
5.5.3. 3.结构内嵌特性:¶
Go语言的结构体内嵌有如下特性。
1.内嵌的结构体可以直接访问其成员变量
例如:ins.a.b.c 的访问可以简化为 ins.C。
内嵌结构体的字段名是它的类型名
内嵌结构体字段仍然可以使用详细的字段进行一层层访问,内嵌结构体的字段名就是它的类型名,代码如下:
var c Color
c.BasicColor,R =1
c.BasicColor,G =1
c.BasicColor,B =1
5.5.4. 4.使用组合思想描述对象特性¶
面对对象的设计原则中,建议对象最好不要使用多重继承。Java和C#就默认禁止了。 组合特性可以快速构建对象的不同特性。比如:人和鸟的特性中,都有行走。
package main
import "fmt"
// 可飞行的,声明飞行结构
type Flying struct{}
// 指针接收器,为飞行结构添加Fly方法
func (f *Flying) Fly() {
fmt.Println("can fly")
}
// 可行走的,声明行走结构
type Walkable struct{}
// 指针接收器,为行走结构添加Walk方法
func (w *Walkable) Walk() {
fmt.Println("can calk")
}
// 声明人类结构
type Human struct {
Walkable // 人类能行走
}
// 声明鸟类结构
type Bird struct {
Flying
Walkable // 鸟类既能飞行也能行走
}
func main() {
// 实例化鸟类
b := new(Bird)
fmt.Println("Bird: ")
// 调用鸟类的Fly()方法
b.Fly()
b.Walk()
// 实例化人类
h :=new(Human)
fmt.Println("Human:")
//调用人类的walk方法
h.Walk()
}
5.5.5. 5.初始化结构体内嵌¶
结构体内嵌初始化时,将结构体内嵌的类型作为字段名像普通结构体一样进行初始化。
package main
import "fmt"
// 车轮
type Wheel struct {
Size int
}
// 引擎
type Engine struct {
Power int // 功率
Type string // 类型
}
// 车
type Car struct {
Wheel
Engine
}
func main() {
c := Car{
// 初始化轮子
Wheel: Wheel{
Size: 18,
},
// 初始化引擎
Engine: Engine{
Power: 100,
Type: "1.4T",
},
}
fmt.Printf("%+v\n", c)
}
//{Wheel:{Size:18} Engine:{Power:100 Type:1.4T}}
5.5.6. 6.初始化内嵌匿名结构体¶
package main
import "fmt"
// 车轮
type Wheel struct {
Size int
}
// 车
type Car struct {
Wheel
// 引擎
Engine struct {
Power int // 功率
Type string // 类型
}
}
func main() {
c := Car{
// 初始化轮子
Wheel: Wheel{
Size: 18,
},
// 初始化引擎
Engine: struct {
Power int
Type string
}{Power: 143, Type: "1.4T"},
}
fmt.Printf("%+v\n", c)
}
//{Wheel:{Size:18} Engine:{Power:100 Type:1.4T}}
原来的Engine结构体被直接定义在Car的结构体中,这种嵌入的写法就是将原来的结构体类型转换为struct{…}
对Car的Engine开始初始化的时候,由于Engine字段的类型没有被单独定义,因此在初始化其字段时需要先填写struct{…}声明其类型。
填充匿名结构体的数据,按”“键:值”格式填充。
6.1成员名字冲突¶
package main
import "fmt"
type A struct {
a int
}
type B struct {
a int
}
type C struct {
A
B
}
func main() {
// 实例化c结构体
c :=&C{}
//c.a = 1 //会报错,因为A结构和B结构中都有一个相同的a。编译器无法区分
c.A.a = 1
c.B.a = 2
fmt.Println(c) //&{{1} {2}}
}
5.5.7. 7. 用外层结构体引用内嵌接口的实例¶
package main
import "fmt"
type Printer interface {
Print()
}
type CanonPrinter struct {
Printname string
}
func (printer CanonPrinter) Print() {
fmt.Println("this is cannoprinter printing now")
}
type PrintWorker struct {
Printer
name string
age int
}
func main() {
canon := CanonPrinter{"canoprint_num1"}
printworker := PrintWorker{
Printer: canon,
name: "hujiangli",
age: 26,
}
printworker.Print()
}
5.5.8. 8.示例:使用匿名结构体分离JSON数据¶
package main
import (
"encoding/json"
"fmt"
)
// 定义手机屏幕
type Screen struct {
Size float32 // 屏幕尺寸
ResX, RexY int // 屏幕水平分辨率
}
type Battery struct {
Capacity int // 容量
}
/*生成json数据*/
func genJsonData() []byte {
// 完整数据结构
/*
定义一个匿名结构体,内嵌了Screen和Battery结构体,同时加入了HasTouchID字段
*/
raw := &struct {
Screen
Battery
HasTouchID bool
}{
//屏幕参数, 为声明的匿名结构体填充数据
Screen: Screen{
Size: 6.6,
ResX: 3,
RexY: 2,
},
// 电池参数
Battery: Battery{
2020,
},
// 是否有指纹识别
HasTouchID: true,
}
// 将数据序列化为JSON
jsonData, _ := json.Marshal(raw)
return jsonData
}
func main() {
// 生成一段json数据
jsonDatat := genJsonData()
fmt.Println(string(jsonDatat)) //{"Size":6.6,"ResX":3,"RexY":2,"Capacity":2020,"HasTouchID":true}
// 只要屏幕和指纹识别信息的结构和实例
screenAndTOuch := struct {
Screen
HasTouchID bool
}{}
// 反序列到screenAndTOuch中
json.Unmarshal(jsonDatat, &screenAndTOuch)
// 输出screenAndTOuch的详细结构
fmt.Printf("%+v\n", screenAndTOuch) //{Screen:{Size:6.6 ResX:3 RexY:2} HasTouchID:true}
// 只需要电池和指纹的结构和实例
batteryAndTOuch := struct {
Battery
HasTouchID bool
}{}
// 反序列到batteryAndTOuch中
json.Unmarshal(jsonDatat, &batteryAndTOuch)
// 输出screenAndTOuch的详细结构
fmt.Printf("%+v\n", batteryAndTOuch) //{Battery:{Capacity:2020} HasTouchID:true}
}