你有对象类,我有结构体,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang结构体(struct)的使用EP06
再续前文,在面向对象层面,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 lang1.18入门精炼教程,由白丁入鸿儒,go lang结构体(struct)的使用EP06的更多相关文章
- 巨细靡遗流程控制,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang流程结构详解EP09
流程结构就是指程序逻辑到底怎么执行,进而言之,程序执行逻辑的顺序.众所周知,程序整体都是自上由下执行的,但有的时候,又不仅仅是从上往下执行那么简单,大体上,Go lang程序的流程控制结构一共有三种: ...
- js中对象 类 实例的区别 数据类型 创建对象
类是对象的具体细分,实例是类中的一个具体事物. 基本数据类型和 引用数据类型 基本数据类型:numble string undefined null 引用数据类型:对象和函数 对象数据类型又细分为:对 ...
- 3.2 STL中的函数对象类模板
*: STL中有一些函数对象类模板,如下所示: 1)例如要求两个double类型的x 和y 的积,可以: multiplies<double>()(x,y); 该表达式的值就是x*y的值. ...
- Object对象类
Object对象类是所有类的祖先,他是默认自动继承的 Java为什么要做一个对象类呢?对象类的目的就是归一了类型,他就是把所有的类所有的对象归纳成为 Object类型.因为对象他认为对象应该拥有一些什 ...
- WorldWind源码剖析系列:可渲染对象类RenderableObject
RenderableObject是WorldWind中所有需要渲染的对象的父类,继承了接口IRenderable和Icomparable.其派生类体系如下所示.RenderableObject的成员如 ...
- ascii#ascii,对象类中找__repr__,获取其返回值
#!/usr/bin/env python #ascii,对象类中找__repr__,获取其返回值 class Foo : def __repr__(self): return "hello ...
- 小学生绞尽脑汁也学不会的python(面对对象-----类与类之间的关系)
小学生绞尽脑汁也学不会的python(面对对象-----类与类之间的关系 1. 依赖关系. 最轻的一种关系 在方法中引入另一个类的对象 class Elephant: def __init__(sel ...
- C++入门到理解阶段二基础篇(9)——C++结构体
1.概述 前面我们已经了解到c++内置了常用的数据类型,比如int.long.double等,但是如果我们要定义一个学生这样的数据类型,c++是没有的,此时就要用到结构体,换言之通过结构体可以帮我们定 ...
- JavaScript 系列--JavaScript一些奇淫技巧的实现方法(二)数字格式化 1234567890转1,234,567,890;argruments 对象(类数组)转换成数组
一.前言 之前写了一篇文章:JavaScript 系列--JavaScript一些奇淫技巧的实现方法(一)简短的sleep函数,获取时间戳 https://www.mwcxs.top/page/746 ...
随机推荐
- 【Axure】母版引发事件
引发事件是指你将母版中某一元件的事件从母版中提升出来,以使其在页面的级别可用. 通过引发事件,可以对在不同页面上母版实例的同一个元件设置不同的交互. 设置引发事件 打开一个母版: 选择其中一个组件: ...
- [刷题] IDA*
BZOJ3041 水叮当的舞步 Description & Solution 见hzw的博客 http://hzwer.com/1507.html Code // Author: wlzhou ...
- 我熬夜读完这份“高分宝典”,竟4面拿下字节跳动offer
前言 怎样的契机? 实际上,目前毕业已经两年时间了,在大学时就已经开始关注字节跳动的发展.一开始,我是电气自动化专业的,大二清楚目标之后就转计算机了,大四进了一家小型的互联网公司实习,具体就不说哪家了 ...
- SQL年龄计算方法
第一种方法: 用DATEDIFF函数,DATEDIFF(YEAR,beginDate,endDate). 测试语句: 1 DECLARE @birthdayDate DATE 2 DECLARE @e ...
- Flink中如何实现一个自定义MetricReporter
什么是 Metrics 在 flink 任务运行的过程中,用户通常想知道任务运行的一些基本指标,比如吞吐量.内存和 cpu 使用情况.checkpoint 稳定性等等.而通过 flink metric ...
- 免费CDN:jsDelivr+Github 使用方法
转自 https://zhuanlan.zhihu.com/p/76951130 本文在CSDN上的链接:https://blog.csdn.net/qq_36759224/article/detai ...
- ACM-由数据范围反推算法复杂度以及算法内容
一般ACM或者笔试题的时间限制是1秒或2秒. 在这种情况下,C++代码中的操作次数控制在 \(10^7\) 为最佳. 下面给出在不同数据范围下,代码的时间复杂度和算法该如何选择: 数据范围 算法选择 ...
- 粗谈对ajax的理解
ajax:Asynchronous JavaScript and XML异步JavaScript和XML技术Asynchronous:JavaScript:XMLHttpRequestXML:实现数据 ...
- 用console画条龙?
相识 console一定是各位前端er最熟悉的小伙伴了,无论是console控制台,还是console对象,做前端做久了,打开一个网页总是莫名自然的顺手打开控制台,有些调皮的网站还会故意在控制台输出一 ...
- Linux下修改RabbitMQ密码
1,首先查看用户列表 rabbitmqctl list_users 2,修改对应用户密码 其中username 为用户名, newpasswd为新密码 rabbitmqctl change_passw ...