你有对象类,我有结构体,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang结构体(struct)的使用EP06

简介: 再续前文,在面向对象层面,Python做到了超神:万物皆为对象,而Ruby,则干脆就是神:飞花摘叶皆可对象。二者都提供对象类操作以及继承的方式为面向对象张目,但Go lang显然有一些特立独行,因为它没有传统的类,也没有继承,取而代之的是结构和组合的方式,也就是结构体(struct)的方式来组织代码,达到类似类的效果。

再续前文,在面向对象层面,Python做到了超神:万物皆为对象,而Ruby,则干脆就是神:飞花摘叶皆可对象。二者都提供对象类操作以及继承的方式为面向对象张目,但Go lang显然有一些特立独行,因为它没有传统的类,也没有继承,取而代之的是结构和组合的方式,也就是结构体(struct)的方式来组织代码,达到类似类的效果。

结构体struct的声明

在 Go lang中使用下面的语法是对结构体的声明:

type struct_name struct {  
    attribute_name1   attribute_type  
    attribute_name2   attribute_type  
    ...  
}

假设定义一个名为 Lesson(课程) 的结构体:

type Lesson struct {  
    name   string //名称  
    target string //学习目标  
    spend  int    //学习花费时间  
}

这里声明了一个结构体类型 Lesson ,它有 name 、 target 和 spend 三个属性,相当于Python中类的私有属性。

也可以把相同类型的属性声明在同一行,这样可以使结构体变得更加紧凑:

type Lesson2 struct {  
    name, target    string  
    spend             int  
}

Lesson 称为命名的结构体(Named Structure) ,这里 Lesson 作为一种新的数据类型而存在,而它可以用于创建 Lesson 类型的结构体变量。

此外,声明结构体时也可以不用声明一个新类型,这样的结构体类型称为匿名结构体(Anonymous Structure) ,可以理解为结构体变量:

var MyLesson struct {  
    name, target    string  
    spend             int  
}

结构体struct的创建

声明了结构体之后,我们可以根据声明好的结构体类型来创建结构体,这个过程有点像Python语言中类的实例化:

import "fmt"  
  
type Lesson struct {  
    name, target string  
    spend        int  
}  
  
func main() {  
    // 使用字段名创建结构体  
    lesson1 := Lesson{  
        name:   "go lang 1.18",  
        target: "学习Go lang,并完成web开发任务",  
        spend:  1,  
    }  
    // 不使用字段名创建结构体  
    lesson2 := Lesson{"go lang 1.18", "学习Go lang,并完成web开发任务", 1}  
  
    fmt.Println("lesson1 ", lesson1)  
    fmt.Println("lesson2 ", lesson2)  
}

程序返回:

lesson1  {go lang 1.18 学习Go lang,并完成web开发任务 1}  
lesson2  {go lang 1.18 学习Go lang,并完成web开发任务 1}

这里字段名可以做省略操作,但要注意传递顺序。

此外,也可以创建匿名结构体:

package main  
  
import "fmt"  
  
func main() {  
    // 创建匿名结构体变量  
    mylesson := struct {  
        name, target string  
        spend        int  
    }{  
        name:   "Go lang 1.18",  
        target: "学习go lang,完成web需求",  
        spend:  1,  
    }  
  
    fmt.Println("mylesson ", mylesson)  
}

当定义好的结构体没有被显式赋值时,结构体的字段将会默认赋为相应类型的零值:

package main  
  
import "fmt"  
  
type Lesson struct {  
    name, target string  
    spend        int  
}  
  
func main() {  
    // 不初始化结构体  
    var lesson = Lesson{}  
  
    fmt.Println("lesson ", lesson)  
}

程序返回:

lesson  {  0}

假设某个或者某几个字段没有赋值,也会默认赋值为对应基本数据类型的零值:

package main  
  
import "fmt"  
  
type Lesson struct {  
    name, target    string  
    spend             int  
}  
  
func main() {  
    // 为结构体指定字段赋初值  
    var lesson = Lesson{  
        name: "go lang 1.18",  
    }  
  
    // 上面的结构体变量 lesson 只初始化了 name 字段, 其他字段没有初始化,所以会被初始化为零值  
    fmt.Println("lesson ", lesson)  
}

程序返回:

lesson  {go lang 1.18  0}

结构体struct的属性与指针

通过点操作符 . 可以访问结构体的属性:

package main  
  
import "fmt"  
  
type Lesson struct {  
    name, target string  
    spend        int  
}  
  
func main() {  
  
    var lesson = Lesson{  
        name: "go lang 1.18",  
    }  
  
    fmt.Println("lesson name ", lesson.name)  
}

程序返回:

lesson name  go lang 1.18

也可以使用点操作符 . 对结构体的字段进行赋值操作:

package main  
  
import "fmt"  
  
type Lesson struct {  
    name, target string  
    spend        int  
}  
  
func main() {  
  
    var lesson = Lesson{  
        name: "go lang 1.18",  
    }  
  
    fmt.Println("lesson name ", lesson.name)  
  
    lesson.name = "Python 3.11"  
  
    fmt.Println("lesson name ", lesson.name)  
  
}

程序返回:

lesson name  go lang 1.18  
lesson name  Python 3.11

需要注意的是,赋值变量和结构体属性的基本数据类型要一致。

在前一篇:借问变量何处存,牧童笑称用指针,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang类型指针(Pointer)的使用EP05我们使用了指针来操作变量,指针也可以指向结构体:

package main  
  
import "fmt"  
  
type Lesson struct {  
    name, target string  
    spend        int  
}  
  
func main() {  
    lesson := &Lesson{"go lang 1.18", "完成对应web需求", 1}  
    fmt.Println("lesson name: ", (*lesson).name)  
    fmt.Println("lesson name: ", lesson.name)  
}

程序返回:

lesson name:  go lang 1.18  
lesson name:  go lang 1.18

lesson是一个指向结构体 Lesson 的指针,上面用 (*lesson).name 访问 lesson的 name 字段,lesson.name 是代替 (*lesson).name 的解引用访问。

在创建结构体时,属性可以只有类型没有属性名,这种属性称为匿名字段(Anonymous Field) :

package main  
  
import "fmt"  
  
type Lesson struct {  
    string  
    int  
}  
  
func main() {  
    lesson := Lesson{"go lang 1.18", 1}  
    fmt.Println("lesson ", lesson)  
    fmt.Println("lesson string: ", lesson.string)  
    fmt.Println("lesson int: ", lesson.int)  
}

程序返回:

lesson  {go lang 1.18 1}  
lesson string:  go lang 1.18  
lesson int:  1

这里程序结构体定义了两个匿名字段,虽然这两个字段没有字段名,但匿名字段的名称默认就是它的类型。所以上面的结构体 Lesoon 有两个名为 string 和 int 的字段,同样需要注意顺序和字段数据类型的匹配问题。

嵌套结构体

结构体本身也支持复合的嵌套结构:

package main  
  
import "fmt"  
  
type Author struct {  
    name string  
}  
  
type Lesson struct {  
    name, target string  
    spend        int  
    author       Author  
}  
  
func main() {  
    lesson := Lesson{  
        name:  "go lang 1.18",  
        spend: 1,  
    }  
    lesson.author = Author{  
        name: "佚名",  
    }  
    fmt.Println("lesson name:", lesson.name)  
    fmt.Println("lesson spend:", lesson.spend)  
    fmt.Println("lesson author name:", lesson.author.name)  
}

程序返回:

lesson name: go lang 1.18  
lesson spend: 1  
lesson author name: 佚名

这里结构体Author本身作为结构体Lesson的一个属性而存在,赋值时,通过父结构体直接调用子结构体名称即可。

如果结构体中有匿名的结构体类型字段,则该匿名结构体里的字段就称为提升字段(Promoted Fields) 。这是因为提升字段就像是属于外部结构体一样,可以用外部结构体直接访问:

package main  
  
import (  
    "fmt"  
)  
  
type Address struct {  
    city, state string  
}  
type Person struct {  
    name string  
    age  int  
    Address  
}  
  
func main() {  
    var p Person  
    p.name = "Naveen"  
    p.age = 50  
    p.Address = Address{  
        city:  "Chicago",  
        state: "Illinois",  
    }  
    fmt.Println("Name:", p.name)  
    fmt.Println("Age:", p.age)  
    fmt.Println("City:", p.city)   //city is promoted field  
    fmt.Println("State:", p.state) //state is promoted field  
}

系统返回:

Name: Naveen  
Age: 50  
City: Chicago  
State: Illinois

如果我们把 Person 结构体中的字段 address 直接用匿名字段 Address 代替, Address 结构体的字段例如 city 就不用像 p.address.city这样访问,而是使用 p.address 就能访问 Address 结构体中的 address字段。现在结构体 Address 有city字段,访问字段就像在 Person 里直接声明的一样,因此我们称之为提升字段,说白了就是把子结构体的字段提升为父结构体的字段,但是定义还是在子结构体之中。

假设结构体的全部属性都是可以比较的,那么结构体也是可以比较的,那样的话两个结构体将可以使用 == 或 != 运算符进行比较。可以通过==运算符或 DeeplyEqual()函数比较两个结构相同的类型并包含相同的字段值:

package main  
  
import (    
    "fmt"  
)  
  
type name struct {    
    firstName string  
    lastName string  
}  
  
  
func main() {    
    name1 := name{"Steve", "Jobs"}  
    name2 := name{"Steve", "Jobs"}  
    if name1 == name2 {  
        fmt.Println("name1 and name2 are equal")  
    } else {  
        fmt.Println("name1 and name2 are not equal")  
    }  
  
    name3 := name{firstName:"Steve", lastName:"Jobs"}  
    name4 := name{}  
    name4.firstName = "Steve"  
    if name3 == name4 {  
        fmt.Println("name3 and name4 are equal")  
    } else {  
        fmt.Println("name3 and name4 are not equal")  
    }  
}

程序返回:

name1 and name2 are equal  
name3 and name4 are not equal

如果结构变量包含的字段是不可比较的,那么结构变量是不可比较的:

package main  
  
import (  
    "fmt"  
)  
  
type image struct {  
    data map[int]int  
}  
  
func main() {  
    image1 := image{data: map[int]int{  
        0: 155,  
    }}  
    image2 := image{data: map[int]int{  
        0: 155,  
    }}  
    if image1 == image2 {  
        fmt.Println("image1 and image2 are equal")  
    }  
}

程序报错:

# command-line-arguments  
.\test.go:18:5: invalid operation: image1 == image2 (struct containing map[int]int cannot be compared)

由此可知,结构体的比较可以理解为其属性的批量比较。

结构体绑定方法

在 Go lang中无法在结构体内部定义方法,这一点与 C 语言类似:

package main  
  
import "fmt"  
  
// Lesson 定义一个名为 Lesson 的结构体  
type Lesson struct {  
    name, target string  
    spend        int  
}  
  
// ShowLessonInfo 定义一个与 Lesson 的绑定的方法  
func (l Lesson) ShowLessonInfo() {  
    fmt.Println("name:", l.name)  
    fmt.Println("target:", l.target)  
}  
  
func main() {  
    l := Lesson{  
        name: "go lang 1.1 8",  
    }  
    l.ShowLessonInfo()  
}

程序返回:

name: go lang 1.1 8  
target:

这里定义了一个与结构体 Lesson 绑定的方法 ShowLessonInfo() ,其中 ShowLessonInfo 是方法名, (l Lesson) 表示将此方法与 Lesson 的实例绑定,这在 Go lang中称为接收者,而 l 表示实例本身,相当于 Python 中的 self ,在方法内可以使用实例本身.属性名称来访问实例属性。

如果绑定结构体的方法中要改变实例的属性时,必须使用指针作为方法的接收者:



package main  
  
import "fmt"  
  
// Lesson 定义一个名为 Lesson 的结构体  
type Lesson struct {  
    name, target string  
    spend        int  
}  
  
// ShowLessonInfo 定义一个与 Lesson 的绑定的方法  
func (l Lesson) ShowLessonInfo() {  
    fmt.Println("spend:", l.spend)  
}  
  
// AddTime 定义一个与 Lesson 的绑定的方法,使 spend 值加 n  
func (l *Lesson) AddTime(n int) {  
    l.spend = l.spend + n  
}  
  
func main() {  
    lesson13 := Lesson{  
        spend: 1,  
    }  
    fmt.Println("添加add方法前")  
    lesson13.ShowLessonInfo()  
    lesson13.AddTime(5)  
    fmt.Println("添加add方法后")  
    lesson13.ShowLessonInfo()  
}  


程序返回:

添加add方法前  
spend: 1  
添加add方法后  
spend: 6

结语

大抵上,Go lang的结构体就是对象类的变种,虽然并没有显性的继承操作,但是通过嵌套结构体和提升字段两种方式,也能达到“继承”的效果,结构体的最终目的和效果与对象类并无二致,类比的话,有点像电脑散热的两种方式:风冷和水冷,我们不能说哪一种方式更好或者不好,只要这种方式可以完成项目中的需求即可,不管黑猫白猫,只要能抓到耗子,就是好猫。

相关文章
|
人工智能 安全 算法
Go入门实战:并发模式的使用
本文详细探讨了Go语言的并发模式,包括Goroutine、Channel、Mutex和WaitGroup等核心概念。通过具体代码实例与详细解释,介绍了这些模式的原理及应用。同时分析了未来发展趋势与挑战,如更高效的并发控制、更好的并发安全及性能优化。Go语言凭借其优秀的并发性能,在现代编程中备受青睐。
395 33
|
7月前
|
Cloud Native 安全 Java
Go语言深度解析:从入门到精通的完整指南
🌟蒋星熠Jaxonic,Go语言探索者。深耕云计算、微服务与并发编程,以代码为笔,在二进制星河中书写极客诗篇。分享Go核心原理、性能优化与实战架构,助力开发者掌握云原生时代利器。#Go语言 #并发编程 #性能优化
626 43
Go语言深度解析:从入门到精通的完整指南
|
编译器 Go
揭秘 Go 语言中空结构体的强大用法
Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
|
8月前
|
Cloud Native 安全 Java
Go语言深度解析:从入门到精通的完整指南
🌟 蒋星熠Jaxonic,执着的星际旅人,用Go语言编写代码诗篇。🚀 Go语言以简洁、高效、并发为核心,助力云计算与微服务革新。📚 本文详解Go语法、并发模型、性能优化与实战案例,助你掌握现代编程精髓。🌌 从goroutine到channel,从内存优化到高并发架构,全面解析Go的强大力量。🔧 实战构建高性能Web服务,展现Go在云原生时代的无限可能。✨ 附技术对比、最佳实践与生态全景,带你踏上Go语言的星辰征途。#Go语言 #并发编程 #云原生 #性能优化
|
10月前
|
JSON 人工智能 Go
在Golang中序列化JSON字符串的教程
在Golang中,使用`json.Marshal()`可将数据结构序列化为JSON格式。若直接对JSON字符串进行序列化,会因转义字符导致错误。解决方案包括使用`[]byte`或`json.RawMessage()`来避免双引号被转义,从而正确实现JSON的序列化与反序列化。
577 7
|
12月前
|
JSON Go C语言
Go语言之定义结构体(Struct)-《Go语言实战指南》
Go 语言中的结构体(`struct`)是一种复合数据类型,可将多个不同类型的字段组合成一个类型。本文介绍了结构体的基本定义、实例创建方式、字段访问与修改、零值特性、比较规则、嵌套使用及标签功能。通过示例代码详细讲解了如何定义和操作结构体,以及其在 JSON 编码等场景的应用。
|
存储 算法 数据可视化
【二叉树遍历入门:从中序遍历到层序与右视图】【LeetCode 热题100】94:二叉树的中序遍历、102:二叉树的层序遍历、199:二叉树的右视图(详细解析)(Go语言版)
本文详细解析了二叉树的三种经典遍历方式:中序遍历(94题)、层序遍历(102题)和右视图(199题)。通过递归与迭代实现中序遍历,深入理解深度优先搜索(DFS);借助队列完成层序遍历和右视图,掌握广度优先搜索(BFS)。文章对比DFS与BFS的思维方式,总结不同遍历的应用场景,为后续构造树结构奠定基础。
604 10
|
存储 Go
Go 语言入门指南:切片
Golang中的切片(Slice)是基于数组的动态序列,支持变长操作。它由指针、长度和容量三部分组成,底层引用一个连续的数组片段。切片提供灵活的增减元素功能,语法形式为`[]T`,其中T为元素类型。相比固定长度的数组,切片更常用,允许动态调整大小,并且多个切片可以共享同一底层数组。通过内置的`make`函数可创建指定长度和容量的切片。需要注意的是,切片不能直接比较,只能与`nil`比较,且空切片的长度为0。
423 3
Go 语言入门指南:切片
|
Web App开发 Go
golang中的类和接口的使用
类使用:实现一个people中有一个sayhi的方法调用功能,代码如下: type People struct { //.. } func (p *People) SayHi() { fmt.
900 0

热门文章

最新文章

推荐镜像

更多