040-struct 结构体(二)
现在你已经基本熟悉 struct 结构体的使用套路了。这一次,我们结合 go 语言的 package 来再来看看 struct 的使用上有什么不同。
使用 package 的目的是让你定义的结构体也能被别人使用~~~比如你发明了一种很牛逼的结构体,不分享给别人岂不是很浪费?
1. 项目结构和程序
你要做的就是创建下面这样的文件夹 struct02,当然了名字随便你了。然后再创建一个 common文件夹,接下来你要做的就是编写 common.go、common_test.go 以及 demo01.go 这三个文件。
图1 目录结构
common 是一个包,下面有一个 common.go 文件,主要内容如下:
// common.go
// 如果你正在抄写这段代码,务必保持所有字段的名字和类型一模一样,大小写也需要一致
package common
type Person struct {
Name string
Age int
hobby string
}
func CreatePerson(name string, age int, hobby string) Person {
return Person{
name,
age,
hobby,
}
}
上面的 common 包,导出了两个符号:Person
和 CreatePerson
,这意味着其他人只要导入了 common 包就可以使用它了。
另一个是测试文件 commont_test.go,内容如下:
package common
import "fmt"
func ExampleOne() {
p := Person{"allen", 20, "read"}
fmt.Println(p)
fmt.Printf("name:%s\n", p.Name)
fmt.Printf("age:%d\n", p.Age)
fmt.Printf("hobby:%s\n", p.hobby)
// Output:
// {allen 20 read}
// name:allen
// age:20
// hobby:read
}
2. 测试 common 包
$ cd common // 进入 common 文件夹
$ go test -v
图2 测试结果
3. 使用你定义的 Person 结构体
好了,现在你已经定义了一个非常牛逼的结构体名为 Person,接下来我需要在其它地方使用到它。参考一下图 1,你需要新建一个 demo01.go 文件:
package main
import "gopl/composite/struct02/common" // 导入你刚刚创建的包
import "fmt"
func main() {
p := common.Person{Name: "allen", Age: 19} // 定义一个 Person 对象
fmt.Printf("%#v\n", p) // 输出对象的内容,# 号含义后面说
fmt.Println(p.Name) // 打印 Name 字段
fmt.Println(p.Age) // 打印 Age 字段
fmt.Println(p.hobby) // 打印 hobby 字段
fmt.Println(p.sex) // 打印性别字段(我们似乎并未定义这个字段)
q := common.CreatePerson("luffy", 18, "eat") // 使用 common 包的函数
fmt.Printf("%#v\n", q)
fmt.Println(q.Name)
fmt.Println(q.Age)
fmt.Println(q.hobby)
}
好了,接下来 go run demo01.go
试试看吧!
图3 运行结果
纳尼?
看起来似乎是编译不通过,看一下输出的内容,说是 hobby 字段 undefined 的。这不对啊, hobby 字段明明已经定义了啊?再看一下括号里的解释,说是无法引用一个未导出的字段或方法 hobby.
再看第二个,sex 字段也是未定义的,这个比较明确,括号里说 type common.Person 没有名为 sex 的字段或者方法,这个完全可以接受吧。
好了,不卖关子了,如果你还记得很久前关于 go 里大小写的那些讨论的话,相信对你来说不难。
4. 导出字段
回顾一下上面 Person 的定义:
type Person struct {
Name string
Age int
hobby string
}
从图 3 中的报错我们可以看出,引用字段 Name 和 Age 是没有问题的,只是引用 hobby 有问题。
Go 中规定,结构体中,只有大写字母开头的字段(或方法)才能被其它包引用。所以我们在 demo01.go 中引用 hobby 字段才会出错,编译都没办法通过。现在我们删除对 hobby 的引用再看看。
package main
import "gopl/composite/struct02/common"
import "fmt"
func main() {
p := common.Person{Name: "allen", Age: 19}
fmt.Printf("%#v\n", p)
fmt.Println(p.Name)
fmt.Println(p.Age)
// fmt.Println(p.hobby) // not ok
// fmt.Println(p.sex) // not ok
q := common.CreatePerson("luffy", 18, "eat")
fmt.Printf("%#v\n", q)
fmt.Println(q.Name)
fmt.Println(q.Age)
// fmt.Println(q.hobby) // not ok
}
图4 运行结果
现在完全没问题了。不过比较神奇的是,fmt.Printf
函数可以把 Person
的所有字段的值给打印出来。
还有一个关于 Printf 的一个格式化副词 #
号,他表示同时输出结构体的字段名称。就比如前面用到的:
fmt.Printf("%#v\n", p)
5. CreatePerson 函数
还剩下最后一个导出函数 CreatePerson 函数没讲,从图 4 的运行结果来看,使用 CreatePerson 函数可以创建一个 Person 结构体,而且还能神奇的给那个未导出字段 hobby
赋值了。
刚刚不是说未导出字段不能赋值吗?你可能漏看了一点内容:只有大写字母开头的字段才能被其它包使用,也就是说其它包不能使用未导出字段,但是并没有规定,自己包内部不能使用啊!回头看看 049 篇,当时定义的结构体字段全部都是小写的,完全没问题啊。
而 CreatePerson
函数本身就是 common 包内部的函数,所以它使用未导出字段,完全没问题~~~
看起来是不是非常像 c++ 里的的 public 和 private 修饰符?
6. 总结
- 导出字段和非导出字段
- 回顾包定义和使用
- 回顾 go test 用法
上一篇: C语言学习——文件——二进制读写