起步
结构体判等
只有在结构体的所有字段类型全部支持直接判等时,才可做判断操作。
map,slice不支持直接判等(即不可比较),需借助reflect.DeepEqual来比较(map整个是一个指针(*hmap, slice是SliceHeader的Data字段是个指针)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package main
import "fmt"
func main() { type data struct { x int }
d1 := data{ x: 100, }
d2 := data{ x: 100, } fmt.Println(d1 == d2) }
|
执行结果为:true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package main
import "fmt"
func main() { type data struct { x int y map[string]int }
d1 := data{ x: 100, }
d2 := data{ x: 100, } fmt.Println(d1 == d2) }
|
编译不通过:invalid operation: d1 == d2 (struct containing map[string]int cannot be compared)
空结构体
空结构struct{}是指没有字段的结构类型。
可作为通道元素类型,用于事件通知。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package main
import ( "fmt" "time" )
func main() { exit := make(chan struct{})
go func() { for { fmt.Println("hello, world!") exit <- struct{}{}
} }()
<-exit
time.Sleep(2e9) fmt.Println("end.") }
|
输出为:
1 2 3
| hello, world! hello, world! end.
|
指针操作
可使用指针直接操作(取值/赋值等)结构体字段,但不能是多级指针。
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
| package main
import ( "fmt" "reflect" )
func main() { type user struct { name string age int }
p := &user{ name: "fliter", age: 26, }
fmt.Println(p)
p.name = "jack" p.age-- fmt.Println(p)
fmt.Println(*p)
p2 := &p fmt.Println(p2) fmt.Println(reflect.TypeOf(p2)) }
|
输出为:
1 2 3 4 5 6
| &{fliter 26} &{jack 25} {jack 25} 0xc00007c010 **main.user
|
结构体互转
Struct中的tag
可以为struct中的每个字段,加一个tag。这个tag可以通过反射机制获取到,最常用场景(包括但绝不仅限于此,如还常用于格式校验,数据库关系映射等)就是 json序列化和反序列化.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package main
import ( "fmt" "reflect" )
type User struct { name string `姓名` sex string `性别` age int `年龄` }
func main() { u := User{"Messi", "男", 32} v := reflect.ValueOf(u) t := v.Type()
for i, n := 0, t.NumField(); i < n; i++ { fmt.Printf("%s为: %v\n", t.Field(i).Tag, v.Field(i)) } }
|
输出为:
1 2 3
| 姓名为: Messi 性别为: 男 年龄为: 32
|
用于json序列化
**注意:**
struct转json时,struct里的字段名首字母必须大写,否则无法正常解析;如果想让struct转json后字段名首字母小写,可以通过tag指定
[更多可点击](https://studygolang.com/articles/13455)
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
| package main
import ( "encoding/json" "fmt" )
type FiveAlevelArea struct { Name string `json:"name"` Location string `json:"address"` Price float32 `json:"entrance ticket"` englishName string `json:"english_name"` }
func main() {
heBei := FiveAlevelArea{ "承德市双桥区承德避暑山庄及周围寺庙景区", "承德市山庄南路", 100.00, "Chengde Imperial Summer Resort", }
j, err := json.Marshal(heBei)
if err != nil { fmt.Println("报错如下:", err) return }
fmt.Println("json字符串为:", string(j))
}
|
输出为:
1
| json字符串为: {"name":"承德市双桥区承德避暑山庄及周围寺庙景区","address":"承德市山庄南路","entrance ticket":100}
|
可见首字母不为大写的字段名,没有被解析出来;最后json字符串中展示出的键名,是tag标签里指定的名称
匿名字段
所谓匿名字段是指没有名字,仅有类型的字段,也称作嵌入字段或嵌入类型。
从编译器角度看,这只是隐式地以类型名作为字段名称。
可直接引用匿名字段的成员,但初始化时必须当作独立字段。
匿名字段不仅仅可以是结构体,除”接口指针”和”多级指针”以外的任何命名类型都可以作为匿名字段。
严格说来,Go并不是传统意义上的面向对象编程语言,或者说仅实现了最小面向对象的机制。
匿名嵌入不是继承,无法实现多态处理。
虽然配合方法集,可用接口来显现一些类似的操作,但其本质完全不同。
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
| package main
import "fmt"
type attr struct { perm int }
type file struct { name string attr }
func main() {
var c file c.name = "my.txt" var a attr a.perm = 777 c.attr = a fmt.Println(c, c.perm)
f := file{ name: "test.dat", attr: attr{ perm: 755, }, } f.perm = 500 fmt.Println(f, f.perm)
}
|
利用匿名字段实现所谓的”继承”
结构体中字段可以没有名字(只有类型),即匿名字段;
如果一个struct(记为A)嵌套了另一个匿名结构体(记为B),那么A结构体可以直接访问
匿名结构体B的方法,从而实现了继承。
参见前文,golang利用组合实现继承,和php或java面向对象的继承有何不同
方法
Golang中的方法作用在特定类型的变量上,因此自定义类型都可以
有方法,而不仅仅是struct
定义:func (recevier type) methodName(参数列表)(返回值列表){}
方法和函数
golang中的”方法”与”函数”的关系,与其他语言有所差别.
方法 给用户定义的类型添加新的行为。方法和函数相比,声明时在关键字func和方法名之间增加了一个参数
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
| package main
import "fmt"
type user struct { name string age int }
func (u user) say() { fmt.Println("hello", u.name) fmt.Println("十年后你的年龄是:", u.age+10) }
func (u *user) run() { fmt.Printf("%s是跑步名将\n", u.name)
}
func main() { c := user{"Trump", 73} c.say()
s := &user{"Bolt", 0}
s.run()
}
|
输出:
1 2 3
| hello Trump 十年后你的年龄是: 83 Bolt是跑步名将
|
- 一个基础类型前面加&后,会变为指针类型;
- *作动词时, 后面必须是一个指针类型,叫做解引用~
即 *(&a) = a
方法作用在结构体上
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
| package main
import ( "fmt" )
type FiveAlevelArea struct { Name string `json:"name"` Location string `json:"address"` Price float32 `json:"entrance ticket"` englishName string `json:"english_name"` }
func (cs FiveAlevelArea) set(Name, Location, englishName string, Price float32) {
cs.Name = Name cs.Location = Location cs.Price = Price cs.englishName = englishName
fmt.Println("当前结构体的值为:", cs) }
func (shuang FiveAlevelArea) get() FiveAlevelArea { return shuang }
func main() { var heBei FiveAlevelArea heBei.set("保定市安新县白洋淀景区", "保定市安新县", "Bai-yang Lake", 40.00)
heBei2 := heBei.get()
fmt.Println(heBei) fmt.Println(heBei2) }
|
输出为:
1 2 3
| 当前结构体的值为: {保定市安新县白洋淀景区 保定市安新县 40 Bai-yang Lake} { 0 } { 0 }
|
因为是值传递,故而原来变量的值没有被修改;
改为指针传递:将第14行set()方法作用的变量(类型)修改为cs *FiveAlevelArea,此时程序的执行结果为:
1 2 3
| 当前结构体的值为: &{保定市安新县白洋淀景区 保定市安新县 40 Bai-yang Lake} {保定市安新县白洋淀景区 保定市安新县 40 Bai-yang Lake} {保定市安新县白洋淀景区 保定市安新县 40 Bai-yang Lake}
|
可能会疑惑,第32行调用set()方法的变量类型为FiveAlevelArea,并不是指针,为何还能编译通过呢?实际上是go自动加了取指针符号&,即第32行:
heBei.init("保定市安新县白洋淀景区", "保定市安新县", "Bai-yang Lake", 40.00)
等同于:
(&heBei).init("保定市安新县白洋淀景区", "保定市安新县", "Bai-yang Lake", 40.00)
方法作用在其他变量上
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
| package main
import "fmt"
type cInt int
func (cui cInt) f1() { fmt.Println("变量值为:", cui) }
func (c *cInt) f2(args cInt) { *c = args + 6 }
func main() {
var a1 cInt = 10086
fmt.Println(a1)
a1.f1()
var a2 cInt
a2.f2(12300) fmt.Println(a2)
(&a2).f2(12300) fmt.Println(a2)
var a3 cInt = 12360 a3.f2(a3) fmt.Println(a3) (&a3).f2(a3) fmt.Println(a3)
}
|
输出为:
1 2 3 4 5 6 7
| 10086 变量值为: 10086 12306 12306 12366 12372
|
String()方法
如果一个变量实现了String()这个方法,那么fmt.Println在输出时,会默认调用这个变量的String()方法。
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
| package main
import "fmt"
type Transportation struct { name string speed string }
func (o *Transportation) Run() {
fmt.Printf("交通工具%s跑起来啦!\n", (*o).name) }
type Train struct { Transportation InventedTime int }
func (cs *Train) String() string { str := fmt.Sprintf("%s的速度可以达到%s\n", cs.name, cs.speed) return str }
func main() { var train Train
train.name = "火车" train.speed = "350km/h" train.InventedTime = 1804
fmt.Println(train)
fmt.Println("------------------") train.Run()
fmt.Println("******************") fmt.Printf("%s", &train)
fmt.Println("##################") fmt.Println("会自动调用String()方法\n", &train)
}
|
输出为:
1 2 3 4 5 6 7 8
| {{火车 350km/h} 1804} ------------------ 交通工具火车跑起来啦! ****************** 火车的速度可以达到350km/h ################## 会自动调用String()方法 火车的速度可以达到350km/h
|
更多关于struct使用中的小tips,可点击
原文链接: https://dashen.tech/2017/11/26/golang之struct入门/
版权声明: 转载请注明出处.