Golang入门教程(十四)结构体和类详解
golang中并没有明确的面向对象的说法,实在要扯上的话,可以将struct比作其它语言中的class。
类声明
1 2 3 4 5 | type Book struct { Title string Author string intro string } |
这样就声明了一个类,其中没有public、protected、private的的声明。golang用另外一种做法来实现属性的访问权限:属性的开头字母是大写的则在其它包中可以被访问,否则只能在本包中访问。类的声明和方法亦是如此。
类方法声明
1 2 3 4 5 6 7 8 9 10 11 | // 类方法声明-传递值对象 func (b Book) B1() { b.Title = "Book001" b.Author = "ErWan" } // 类方法声明-传递指针对象 func (b *Book) B2() { b.Title = "Book002" b.Author = "Tinywan" } |
和其它语言不一样,golang声明方法和普通方法一致,只是在func后增加了b Book这样的声明。加和没有加*的区别在于一个是传递指针对象(加*),一个是传递值对象。
传递指针和对象的区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | package main import "fmt" // 类声明 type Book struct { Title string Author string DateTime string } // 类方法声明-传递值对象 func (b Book) B1() { b.Title = "Book001" b.Author = "ErWan" } // 类方法声明-传递指针对象 func (b *Book) B2() { b.Title = "Book002" b.Author = "Tinywan" } func main() { /*声明一个 Book 类型的变量 b ,并调用 B1() 和 B2()*/ b := Book{ "Def-Book" , "Def-Author" , "Def-DateTime" } fmt .Println( "B1 调用前:" , b.Title, b.Author, b.DateTime) b.B1() fmt .Println( "B1 调用后:" , b.Title) fmt .Println( "------------------\r\n" ) fmt .Println( "B2 调用前:" , b.Title) b.B2() fmt .Println( "B2 调用后:" , b.Title) } |
执行结果:
B1() 的接收者是值类型 Book, B2() 的接收者是值类型 *Book , 两个方法内都是改变Name值。
小结:
1、接收者可以看作是函数的第一个参数,即这样的: func B1(b Book), func B2(b *Book)。 go不是面向对象的语言,所以用那种看起来像面向对象的语法来理解可能有偏差。
2、当调用 b.B1() 时相当于 B1(b) ,实参和行参都是类型 Book,可以接受。此时在B1()中的b只是 "Def-Book" 的值拷贝,所以B1()的修改影响不到" Def-Book"。
3、当调用 b.B2() => B2(b1),这是将 Book 类型传给了 *Book 类型,go可能会取 b 的地址传进去: B2(&b)。所以 B2() 的修改可以影响 b 。
例如在一个beego 框架中我们要修改一个action的值,是这么定义的(为了修改内部结构的值。而不是传递一下)
1 2 3 4 5 | func (this *InputController) InputGet(){ // Get 方式接受 name := this.GetString( "name" ) // 不使用模版,直接用 this.Ctx.WriteString 输出字符串 this.Ctx.WriteString(name) } |
值类型不可以改变值,而指针类型则是可以的
匿名结构体
1 2 3 4 5 | p := struct { Name string Gender string Age uint8 }{ "Robert" , "Male" , 33} |
匿名结构体最大的用处是在内部临时创建一个结构以封装数据,而不必正式为其声明相关规则。
实例化对象
实例化对象有好几种方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | package main // 类声明 type Person struct { Name string Age int Doc []string // slice切片 } // 类方法声明-传递值对象 func (p *Person) P1() { p.Name = "Tinywan" p.Age = 24 } // 类方法声明-传递指针对象 func (p *Person) P2() { p.Name = "Tinyaiai" p.Age = 22 } func main() { // 实例化对象 实例化的时候可以初始化属性值,如果没有指明则默认为系统默认值 p1 := &Person{} p1.Name = "ShaoBo Wan" p1.Age = 20 p2 := &Person{Name: "HaHa" } p3 := new(Person) p3.Name = "New Name" p4 := Person{} p4.Name = "Name 001" p4.Age = 26 p5 := Person{Name: "Name 002" ,Age:28} // 使用中如果包含数组(动态数组 slice切片),结构体的实例化需要加上类型如上如果intro的类型是[]string p6 := &Person{ "zhangsan" , 25, []string{ "lisi" , "wangwu" }, } } |
注意,最后一个实例化
1 2 3 4 5 | p6 := &Person{ "zhangsan" , 25, []string{ "lisi" , "wangwu" }, } |
小结:
1、使用中如果包含数组,结构体的实例化需要加上类型如上如果Doc的类型是[]string。
2、实例化的时候可以初始化属性值,如果没有指明则默认为系统默认值。
3、加&符号和new的是指针对象,没有的则是值对象,这点和php、java不一致,在传递对象的时候要根据实际情况来决定是要传递指针还是值。
4、当对象比较小的时候传递指针并不划算。
继承
确切的说golang中叫做组合(composition)
1、先初始化为空再赋值
1 2 3 | // 先初始化为空再赋值 s1 := &Student{} s1.schoole = "QiHua" |
2、直接赋值
1 2 3 4 5 6 7 8 9 | // 直接赋值 s2 := &Student{ Persons: Persons{ Name: "Tinywan" , Age:24, Doc:[]string{ "H1" , "h2" }, }, schoole: "BeiJin Schoole" , } |
3、完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | package main import ( "fmt" ) // 类声明 type Persons struct { Name string Age int Doc []string // slice切片 } // 获取Name func (p *Persons) getName() { fmt .Println( "Name is " ,p.Name) } type Student struct { // Student 属性中声明了 Persons,表示组合了Persons 的属性和方法(属性和方法都会被继承) Persons Name string schoole string } func main() { // 先初始化为空再赋值 s1 := &Student{} // 当访问Name的时候默认为ProsePoem的 Name,如果需要访问Persons 的 Name 属性可以使用 s1.Persons.Name 来访问方法同理。 s1.Name = "Tinywan" s1.Persons.Name = "Persons Name" s1.schoole = "QiHua" fmt .Println( "s1 = " ,s1) fmt .Println( "\r\n" ) // 直接赋值 s2 := &Student{ Persons: Persons{ Name: "Tinywan" , Age:24, Doc:[]string{ "s2-Doc" , "s2-Doc" }, }, Name: "ErWan" , schoole: "BeiJin-Schoole" , } fmt .Println( "s2 = " ,s2) } |
方法的继承和属性一致
参考
1、https://segmentfault.com/a/1190000012325027
2、
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构