文章目录
- Go 语言进阶:构造函数、父子结构体与组合复用详解
- 一、Go 中的构造函数(无官方关键字,约定实现)
- 1.1 核心概念
- 1.2 构造函数命名规范(业界统一)
- 1.3 基础构造函数示例
- 1.4 带默认值的构造函数(工程常用,解决无重载问题)
- 1.5 为什么构造函数优先返回指针
- 二、父子结构体(基于匿名成员实现组合,替代继承)
- 2.1 前置回顾
- 2.2 父子结构体基础定义示例
- 2.3 父子结构体 + 构造函数结合(工程标准写法)
- 2.4 父子结构体同名字段冲突(就近原则)
- 2.5 方法提升:父结构体方法,子结构体直接调用
- 三、核心总结(构造函数 + 父子结构体)
Go 语言进阶:构造函数、父子结构体与组合复用详解
上一篇我们完整学习了结构体指针、new 关键字、匿名结构体、匿名成员基础概念,其中匿名成员是实现父子结构体复用的核心前提。
本篇继续无缝衔接,重点讲解 Go 中构造函数的设计思想与标准写法,同时说明 Go无函数重载的特性,以及利用匿名成员实现父子结构体(结构体组合),完成 Go 面向对象式的代码复用,替代传统面向对象的继承。
一、Go 中的构造函数(无官方关键字,约定实现)
1.1 核心概念
Go 语言没有class类,也没有constructor构造函数关键字,不能像 Java/C++ 一样直接定义构造方法。
工程上约定:使用普通函数模拟构造函数,专门用来实例化结构体、初始化字段,统一返回结构体指针。
补充重要知识点:Go 语言不支持函数重载
函数重载:指函数名相同、参数不同,实现多个同名函数。
Go 中不允许两个同名函数,哪怕参数不一样也不行,因此不能通过重载实现多版本构造函数,一般使用可变参数、不同函数名实现多构造逻辑。
1.2 构造函数命名规范(业界统一)
- 函数名以
New开头:New结构体名(),例如NewUser()、NewAnimal() - 入参:接收需要初始化的字段
- 返回值:结构体指针(优先指针,避免值拷贝,方便后续修改)
1.3 基础构造函数示例
packagemainimport"fmt"// 定义用户结构体typeUserstruct{NamestringAgeint}// 构造函数:NewUser,返回 *User 指针funcNewUser(namestring,ageint)*User{// 内部使用 new 或字面量取地址初始化return&User{Name:name,Age:age,}}funcmain(){// 直接调用构造函数实例化u:=NewUser("张三",22)fmt.Printf("用户信息:%+v,内存地址:%p\n",u,u)}终端输出结果:
用户信息:&{Name:张三 Age:22},内存地址:0xc000010200代码解释:
- 定义
User普通结构体,包含姓名、年龄两个字段 NewUser作为构造函数,接收姓名、年龄参数,返回结构体指针main中直接调用构造函数创建对象,打印完整信息与内存地址- 返回指针避免值拷贝,外部可直接修改原结构体数据
1.4 带默认值的构造函数(工程常用,解决无重载问题)
因为 Go没有函数重载,不能写两个NewUser,所以业务中常用可变参数实现可选传参、默认值逻辑:
packagemainimport"fmt"typeUserstruct{NamestringAgeint}// 年龄不传默认 18 岁,用可变参数实现多构造效果funcNewUser(namestring,age...int)*User{defaultAge:=18iflen(age)>0{defaultAge=age[0]}return&User{Name:name,Age:defaultAge,}}funcmain(){u1:=NewUser("李四")// 年龄默认18u2:=NewUser("王五",25)fmt.Println(u1,u2)}终端输出结果:
&{李四 18} &{王五 25}代码解释:
- 使用可变参数
age ...int模拟多构造逻辑,规避 Go 不支持函数重载的限制 - 未传入年龄时,默认赋值 18 岁
- 传入年龄则使用自定义值,统一初始化逻辑,减少重复代码
1.5 为什么构造函数优先返回指针
- 避免结构体值拷贝,提升性能
- 外部拿到指针后,可调用指针接收者方法修改原数据
- 符合 Go 工程开发规范,统一内存使用方式
二、父子结构体(基于匿名成员实现组合,替代继承)
2.1 前置回顾
上一节我们学习了匿名成员基础概念:只有类型、没有字段名。
当一个结构体,把另一个结构体作为匿名成员嵌入时,就形成了父子结构体:
- 父结构体:被嵌入的公共基础结构体
- 子结构体:嵌入父结构体、扩展新字段的业务结构体
Go 语言没有继承,不支持
extends,通过**结构体组合(匿名成员嵌入)**实现代码复用。
2.2 父子结构体基础定义示例
以动物为父结构体,狗、猫为子结构体演示:
packagemainimport"fmt"// 父结构体:公共基础结构体(父类)typeAnimalstruct{Eatstring// 食物Colorstring// 颜色Ageint// 年龄}// 子结构体 Dog:匿名嵌入父结构体 Animal,扩展自己的字段typeDogstruct{Animal// 匿名成员,嵌入父结构体,实现父子关系Breedstring// 狗独有:品种}// 子结构体 Cat:同样嵌入父结构体typeCatstruct{Animal Characterstring// 猫独有:性格}funcmain(){// 实例化子结构体 Dogdog:=Dog{Animal:Animal{Eat:"骨头",Color:"黄色",Age:3,},Breed:"金毛",}// 直接访问父结构体字段(字段提升)fmt.Println(dog.Eat)fmt.Println(dog.Color)fmt.Println(dog.Breed)}终端输出结果:
骨头 黄色 金毛代码解释:
Animal为父结构体,存放所有动物公共属性Dog、Cat作为子结构体,通过匿名成员嵌入父结构体- 父结构体字段自动提升,子结构体可直接点调用,无需嵌套
2.3 父子结构体 + 构造函数结合(工程标准写法)
给父、子结构体分别定义构造函数,统一初始化:
packagemainimport"fmt"// 父结构体typeAnimalstruct{EatstringColorstringAgeint}// 父结构体构造函数funcNewAnimal(eat,colorstring,ageint)*Animal{return&Animal{Eat:eat,Color:color,Age:age,}}// 子结构体 DogtypeDogstruct{Animal Breedstring}// 子结构体构造函数:内部调用父构造函数funcNewDog(eat,colorstring,ageint,breedstring)*Dog{return&Dog{Animal:*NewAnimal(eat,color,age),Breed:breed,}}funcmain(){dog:=NewDog("骨头","黑色",2,"哈士奇")fmt.Printf("狗信息:%+v\n",dog)fmt.Println("直接访问父字段:",dog.Eat,dog.Color)}终端输出结果:
狗信息:{Animal:{Eat:骨头 Color:黑色 Age:2} Breed:哈士奇} 直接访问父字段: 骨头 黑色代码解释:
- 父、子分别定义构造函数,各司其职
- 子构造函数内部调用父构造函数,完成公共字段初始化
- 外部只需调用子构造函数,即可完成全部属性赋值,代码高度解耦
2.4 父子结构体同名字段冲突(就近原则)
如果子结构体和父结构体存在同名字段,访问时优先使用子结构体自身字段;如需访问父结构体字段,显式指定父结构体类型。
packagemainimport"fmt"typeAnimalstruct{NamestringAgeint}typeDogstruct{Animal Namestring// 和父结构体字段同名}funcmain(){d:=Dog{Animal:Animal{Name:"动物",Age:3},Name:"小狗",}fmt.Println(d.Name)fmt.Println(d.Animal.Name)}终端输出结果:
小狗 动物代码解释:
- 父子结构体存在同名字段
Name - 直接访问默认优先子结构体(就近原则)
- 访问父结构体同名字段,必须显式指定父结构体名
2.5 方法提升:父结构体方法,子结构体直接调用
不仅字段会提升,父结构体绑定的方法也会自动提升,子结构体实例可直接调用父结构体方法,完美实现复用。
packagemainimport"fmt"typeAnimalstruct{Namestring}// 父结构体绑定方法func(a*Animal)Speak(){fmt.Printf("%s 发出叫声\n",a.Name)}typeDogstruct{Animal}funcmain(){d:=Dog{Animal:Animal{Name:"旺财"}}// 子结构体直接调用父结构体方法(方法提升)d.Speak()}终端输出结果:
旺财 发出叫声代码解释:
- 父结构体绑定
Speak方法 - 方法随匿名成员自动提升,子结构体无需重写即可调用
- 实现方法复用,替代传统面向对象的继承逻辑
三、核心总结(构造函数 + 父子结构体)
- 构造函数:Go 约定用
NewXxx()函数模拟,返回结构体指针,用于统一初始化、设置默认值,是项目标配。 - 函数重载:Go不支持函数重载,多构造场景用可变参数或不同函数名实现。
- 父子结构体本质:通过匿名成员嵌入父结构体实现组合,替代传统继承,无强耦合。
- 字段/方法提升:父结构体字段、方法自动提升到子结构体,直接调用,简化代码。
- 同名冲突规则:就近原则,优先子结构体,显式指定父结构体可访问父字段。
至此,Go 从结构体基础 → 指针 → new → 匿名结构体/成员 → 构造函数 → 父子结构体完整体系全部讲完,完全覆盖企业级开发中结构体的所有核心用法。