写在前面
在上一文《Golang入门学习之函数》当中,我们学习了Golang当中函数的用法。接下来,我们将一起来学习Golang的结构体。
结构体的定义
结构体
(struct)类似于其他面向对象当中类
(class)的概念,但它和类有着明显的不同。结构体是复合类型
(composite types),它由一系列属性组成,这些属性对应着不同的值与类型。组成结构体类型的那些数据称为字段
(fields).
Golang通过结构体的形式支持用户自定义类型,或者叫定制类型。
定义方式:
type StructName struct {
field1 type1
field2 type2
...
}
结构体的实例化
使用new()
我们可以使用 new 函数给一个新的结构体变量分配内存,它返回指向已分配内存的指针。
var t *StructName
t = new(StructName)
请看下面这个例子:
首先,我们在go_code/struct/model
这个包当中,定义了一个结构体Immortal
表示修仙者,并在main
当中引入这个包,并使用Immortal
go_code/struct/model/immortal.go
:
package model
// 修仙者
type Immortal struct {
Name string
Age int
Gender string
}
main中:
package main
import (
"go_code/struct/model"
"strconv"
)
func main() {
// i是指向model.Immortal结构体变量的指针
var i *model.Immortal
i= new(model.Immortal)
i.Age = 500
i.Name = "韩立"
i.Gender = "男"
println(i.Name, strconv.Itoa(i.Age)+"岁",i.Gender)
}
输出:
韩立 500岁 男
我们可以通过点符号获取结构体的值或给其赋值,点符号在Golang中称之为选择器符(selector-notation)。
使用混合字面量语法
初始化一个结构体实例的更简短和惯用的方式如下:
// i的类型是 *model.Immortal
var i = &model.Immortal{"南宫婉",18,"女"}
// 或者
var i model.Immortal
i := model.Immortal{"南宫婉",18,"女"}
上面这两个例子当中的写法(&model.Immortal{"南宫婉",18,"女"}
)称之为混合字面量语法
(composite literal sysntax),这是一种简写,底层仍然会调用new函数。在这种写法当中,需要注意的一点是值的顺序必须按照字段顺序来写。表达式new(Type)
和&Type()
是等价的。
Golang中结构体的特点
内存布局
Go 语言中,结构体和它所包含的数据在内存中是以连续块的形式存在的,即使结构体中嵌套有其他的结构体,这在性能上带来了很大的优势。
递归结构体
结构体类型可以通过引用自身来定义。这在定义链表或二叉树的元素(通常叫节点)时特别有用,此时节点包含指向临近节点的链接(地址)。
package model
type Node struct {
data string
next *Node
}
其中,data字段当中存储的是当前节点的数据,next是指向下一个节点的指针
结构体的转换
Go 中的类型转换遵循严格的规则。当为结构体定义了一个 alias
类型时,此结构体类型和它的 alias 类型都有相同的底层类型。
请看下面的这个例子:
number.go
package model
type Number struct {
Value float32
}
conversion.go
:
package main
import "go_code/struct/model"
func main() {
var a = myNumber{Value: 18.0}
b := model.Number(a)
println(b.Value)
}
type myNumber model.Number
使用工厂方法创建结构体实例
Golang不支持其他面向对象编程语言(例如Java)那样的构造方法。我们通常通过定义一个构造工厂方法用于创建结构体实例。
请看下面的例子:
package model
// 修仙者
type Immortal struct {
Name string
Age int
Gender string
}
func NewImmortal(age int, name, gender string) *Immortal {
if age<0 {
return nil
}
return &Immortal{Name: name,Gender: name,Age: age}
}
对于工厂方法的命名,我们一般以New
或new
开头,它返回一个指向结构体实例的指针
如何强制使用工厂方法而不是直接new?
在讲这个问题之前,需要给大家补充一个知识点,那就是Golang当中包的可见性规则:
在Golang中,当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 private )。
因此,为了达到我们的目的,我们需要这样去定义我们的结构体:
package v2
type immortal struct {
name string
age int
gender string
}
func NewImmortal(age int, name, gender string) *immortal {
if age < 0 {
return nil
}
return &immortal{
name: name,
age: age,
gender: gender,
}
}
带标签的结构体
结构体中的字段除了有名字和类型外,还可以有一个可选的标签(tag):它是一个附属于字段的字符串,可以是文档或其他的重要标记。标签的内容不可以在一般的编程中使用,只有包 reflect
能获取它。在gorm(一个Golng的orm框架,类似于Java当中的mybatis
)当中使用tag标记字段,从而起到映射数据库表字段的作用。
请看下面这个例子:
package tag
import (
"fmt"
"reflect"
)
type Immortal struct {
Name string "休仙者的名字"
Age int "修仙者的年龄"
Gender string "修仙者的性别"
}
func PrintTag(im Immortal, i int) {
imm :=reflect.TypeOf(im)
value :=imm.Field(i)
fmt.Println(value.Tag)
}
package main
import "go_code/struct/model/tag"
func main() {
var immortal = tag.Immortal{Name: "南宫婉", Age: 18, Gender: "女"}
for i:=0; i<3; i++ {
tag.PrintTag(immortal,i)
}
}
输出:
休仙者的名字
修仙者的年龄
修仙者的性别
匿名字段和内嵌结构体
结构体可以包含一个或多个 匿名(或内嵌)字段,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型就是字段的名字。匿名字段本身可以是一个结构体类型,即 结构体可以包含内嵌结构体。在Golang当中,通过组合来实现其他面向对象编程语言当中的继承。
请看下面的例子
package main
import "fmt"
type immortal struct {
// 姓名
string
// 年龄
int
// 修仙境界
level
}
type level struct {
// 境界名称
string
// 灵气值
float32
}
func main() {
var im = immortal{
string: "韩立",
int: 500,
level: level{
"练气七层",
7800.0,
},
}
fmt.Println("======修仙者资料卡======")
fmt.Println("姓名:",im.string)
fmt.Println("年龄:",im.int)
fmt.Println("------境界信息---------")
fmt.Println("境界名称:",im.level.string)
fmt.Println("境界灵气值:",im.level.float32)
fmt.Println("------境界信息---------")
fmt.Println("======修仙者资料卡======")
}
输出:
======修仙者资料卡======
姓名: 韩立
年龄: 500
------境界信息---------
境界名称: 练气七层
境界灵气值: 7800
------境界信息---------
======修仙者资料卡======
写在最后
关于Golang结构体的学习就写到这里,本文当中涉及到的例子可以点击此处下载。如果我的学习笔记能够给你带来帮助,还请多多点赞鼓励。文章如有错漏之处还请各位小伙伴帮忙斧正。