写在前面:
0x01 — Struct使用
Struct结构体的关键字是struct,定义时可以使用type进行定义:
type person struct {
sex int
age int
name string
}
定义一个struct方式和其他语言的class类似, 定义成员变量时记得不要加逗号 ,成员名称后面需要根成员类型。
使用时需要先声明后初始化
package main
import (
"testing"
)
// 个人结构
type person struct {
sex int
age int
name string
}
type Addr struct {
name string
city string
addr string
}
// 以家庭为示例,包含家庭创建时间,家庭成员,父亲,母亲,孩子
type Family struct {
Addr // 此处为类似继承功能,Family将会拥有Addr下的所有成员属性,
//Addr Addr // 这种写法是标准写法,如果引用名称和类型一致,则可以省略类型
marryTime int
father person
mather *person
children []*person
}
//结构体使用
func TestStruct(t *testing.T) {
var tf1 Family
tf1.marryTime = 10
tf1.addr = "银河系 太阳系 地球市 中国01号" // 在定义Family时没有定义addr,但是我们可以额外定义,定义后在Family生命周期生效。
tf1.Addr = Addr{"银河","太阳","地球1号"}
tf1.mather = &person{1,30, "mama"}
tf1.father = person{sex: 2, age: 32, name:"papa"}
tf1.children = []*person{
{2,2, "tom"}, // 因为明确要获取结构体指针的数组,所以可以省略名称
&person{2,5, "davy"},
}
t.Log("打印Tf1:", tf1) // 可以打印出tf1的机构
t.Log("tf1 家庭的地址:", tf1.addr)
t.Log("tf1 家庭的地址Addr.name:", tf1.Addr.name)
t.Log("tf1 家庭的地址Addr.city:", tf1.Addr.city)
t.Log("tf1 家庭的地址Addr.addr:", tf1.Addr.addr)
t.Logf("tf1 家庭建立已经 %d 年了", tf1.marryTime)
t.Log("tf1 家庭的父亲:", tf1.father.name)
t.Log("tf1 家庭的母亲:", tf1.mather.name)
for _,i := range tf1.children { // 此处索引不需要则使用_来代替
t.Log("tf1 家庭的孩子:", i.name)
}
}
创建方式就是如上,创建父亲和母亲的实体时,可以不需要指明成员名称,如目前,可以直接把值加进去,也可以想申请父亲实体一样指明成员的名称。
看到这里可能会有疑问,为什么父亲和母亲签名有没有星号结果都是一样的呢?关于指针的介绍,我会专门重点介绍下,目前只需要知道在结构体中,访问结构体的成员和访问结构体指针的成员方式是一样的就可以。
0x02 — Struct Tag
结构体和JSON的结构很像, JSON是一种用于发送和接收结构化信息的标准协议 , 对JavaScript中各种类型的值——字符串、数字、布尔值和对象——Unicode本文编码,它可以用有效可读的方式表示基础数据类型和数组、slice、结构体和map等聚合数据类型。JSON的对象类型可以用于编码Golang的map类型(key类型是字符串)和结构体。
// struct tag
type ProductLicense struct {
SystemID uint `json:"system_id"` //系统id
Name string `json:"name"` //服务名
Key string `json:"key"` //服务key
UsageLimit int64 `json:"usage_limit"` //使用次数
IsActive bool //是否生效
}
func TestPl(t *testing.T) {
plList := []ProductLicense{
{21, "内存服务", "BX-0101", 10000, true},
{22, "磁盘服务", "BX-0102", 10000, true},
{23, "认证服务", "BX-0103", 10000, true},
{24, "工具服务", "BX-0104", 10000, false},
}
d,err := json.Marshal(plList)
if err == nil {
t.Logf("服务认证控制:\n%s" ,d)
} else {
t.Log("结构无法转换")
}
// 下面这段代码是推荐写法,融合了GO的特色,条件中赋值,同时进行校验,还保持了快速错误抛出的原则
//if d, err := json.Marshal(plList); err != nil {
// t.Log("结构无法转换")
//} else {
// t.Logf("服务认证控制:\n%s" ,d)
//}
}
输出:
=== RUN TestPl
struct_test.go:64: 服务认证控制:
[{"system_id":21,"name":"内存服务","key":"BX-0101","usage_limit":10000,"IsActive":true},
{"system_id":22,"name":"磁盘服务","key":"BX-0102","usage_limit":10000,"IsActive":true},
{"system_id":23,"name":"认证服务","key":"BX-0103","usage_limit":10000,"IsActive":true},
{"system_id":24,"name":"工具服务","key":"BX-0104","usage_limit":10000,"IsActive":false}]
--- PASS: TestPl (0.00s)
PASS
上面代码中ProductLicense结构体中,成员后面通过“符号进行了tag标记,在转换成json时,使用后面的名字代替,如果没有声明tag,则使用原名称作为json中的名称(IsActive)。
大家应该发现,isDelete转换json时,输出中没有这个成员,这是因为成员变量为小写默认为私有变量,不能进行转换,只有首字母大写的成员才会被转换,这个逻辑贯穿整个Golang体系。
0x03 — type 声明
在Golang可以通过type来自定义一种类型
type Centimeters float64 // 厘米
type Decimeter float64 // 分米
const (
centt Centimeters = -273.15
Deci Decimeter = 0
)
我们可以通过type关键字声明两个类型一个Centimeters,一个是Decimeter,两个类型都是float64类型, 它们虽然有着相同的底层类型float64,但是它们是不同的数据类型,因此它们不可以被相互比较或混在一个表达式运算 ,两个类型不可隐性转换。
0x03 — 总结
Struct结构体使用中要注意类似继承的结构,虽然实现了类似继承的功能,但是不完全是继承的概念,后续在将方法时会明确。
结构体在后面学习框架比如grom等的时候,会遇到特殊的tag类型,原理都是类似,只需要多多实践。