Merging other languages
This commit is contained in:
427
en/02.4.md
427
en/02.4.md
@@ -1,213 +1,214 @@
|
||||
# 2.4 struct类型
|
||||
## struct
|
||||
Go语言中,也和C或者其他语言一样,我们可以声明新的类型,作为其它类型的属性或字段的容器。例如,我们可以创建一个自定义类型`person`代表一个人的实体。这个实体拥有属性:姓名和年龄。这样的类型我们称之`struct`。如下代码所示:
|
||||
|
||||
type person struct {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
看到了吗?声明一个struct如此简单,上面的类型包含有两个字段
|
||||
- 一个string类型的字段name,用来保存用户名称这个属性
|
||||
- 一个int类型的字段age,用来保存用户年龄这个属性
|
||||
|
||||
如何使用struct呢?请看下面的代码
|
||||
|
||||
type person struct {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
|
||||
var P person // P现在就是person类型的变量了
|
||||
|
||||
P.name = "Astaxie" // 赋值"Astaxie"给P的name属性.
|
||||
P.age = 25 // 赋值"25"给变量P的age属性
|
||||
fmt.Printf("The person's name is %s", P.name) // 访问P的name属性.
|
||||
除了上面这种P的声明使用之外,还有另外几种声明使用方式:
|
||||
|
||||
- 1.按照顺序提供初始化值
|
||||
|
||||
P := person{"Tom", 25}
|
||||
|
||||
- 2.通过`field:value`的方式初始化,这样可以任意顺序
|
||||
|
||||
P := person{age:24, name:"Tom"}
|
||||
|
||||
- 3.当然也可以通过`new`函数分配一个指针,此处P的类型为*person
|
||||
|
||||
P := new(person)
|
||||
|
||||
下面我们看一个完整的使用struct的例子
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
|
||||
// 声明一个新的类型
|
||||
type person struct {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
|
||||
// 比较两个人的年龄,返回年龄大的那个人,并且返回年龄差
|
||||
// struct也是传值的
|
||||
func Older(p1, p2 person) (person, int) {
|
||||
if p1.age>p2.age { // 比较p1和p2这两个人的年龄
|
||||
return p1, p1.age-p2.age
|
||||
}
|
||||
return p2, p2.age-p1.age
|
||||
}
|
||||
|
||||
func main() {
|
||||
var tom person
|
||||
|
||||
// 赋值初始化
|
||||
tom.name, tom.age = "Tom", 18
|
||||
|
||||
// 两个字段都写清楚的初始化
|
||||
bob := person{age:25, name:"Bob"}
|
||||
|
||||
// 按照struct定义顺序初始化值
|
||||
paul := person{"Paul", 43}
|
||||
|
||||
tb_Older, tb_diff := Older(tom, bob)
|
||||
tp_Older, tp_diff := Older(tom, paul)
|
||||
bp_Older, bp_diff := Older(bob, paul)
|
||||
|
||||
fmt.Printf("Of %s and %s, %s is older by %d years\n",
|
||||
tom.name, bob.name, tb_Older.name, tb_diff)
|
||||
|
||||
fmt.Printf("Of %s and %s, %s is older by %d years\n",
|
||||
tom.name, paul.name, tp_Older.name, tp_diff)
|
||||
|
||||
fmt.Printf("Of %s and %s, %s is older by %d years\n",
|
||||
bob.name, paul.name, bp_Older.name, bp_diff)
|
||||
}
|
||||
|
||||
### struct的匿名字段
|
||||
我们上面介绍了如何定义一个struct,定义的时候是字段名与其类型一一对应,实际上Go支持只提供类型,而不写字段名的方式,也就是匿名字段,也称为嵌入字段。
|
||||
|
||||
当匿名字段是一个struct的时候,那么这个struct所拥有的全部字段都被隐式地引入了当前定义的这个struct。
|
||||
|
||||
让我们来看一个例子,让上面说的这些更具体化
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
|
||||
type Human struct {
|
||||
name string
|
||||
age int
|
||||
weight int
|
||||
}
|
||||
|
||||
type Student struct {
|
||||
Human // 匿名字段,那么默认Student就包含了Human的所有字段
|
||||
speciality string
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 我们初始化一个学生
|
||||
mark := Student{Human{"Mark", 25, 120}, "Computer Science"}
|
||||
|
||||
// 我们访问相应的字段
|
||||
fmt.Println("His name is ", mark.name)
|
||||
fmt.Println("His age is ", mark.age)
|
||||
fmt.Println("His weight is ", mark.weight)
|
||||
fmt.Println("His speciality is ", mark.speciality)
|
||||
// 修改对应的备注信息
|
||||
mark.speciality = "AI"
|
||||
fmt.Println("Mark changed his speciality")
|
||||
fmt.Println("His speciality is ", mark.speciality)
|
||||
// 修改他的年龄信息
|
||||
fmt.Println("Mark become old")
|
||||
mark.age = 46
|
||||
fmt.Println("His age is", mark.age)
|
||||
// 修改他的体重信息
|
||||
fmt.Println("Mark is not an athlet anymore")
|
||||
mark.weight += 60
|
||||
fmt.Println("His weight is", mark.weight)
|
||||
}
|
||||
|
||||
图例如下:
|
||||
|
||||

|
||||
|
||||
图2.7 struct组合,Student组合了Human struct和string基本类型
|
||||
|
||||
我们看到Student访问属性age和name的时候,就像访问自己所有用的字段一样,对,匿名字段就是这样,能够实现字段的继承。是不是很酷啊?还有比这个更酷的呢,那就是student还能访问Human这个字段作为字段名。请看下面的代码,是不是更酷了。
|
||||
|
||||
mark.Human = Human{"Marcus", 55, 220}
|
||||
mark.Human.age -= 1
|
||||
|
||||
通过匿名访问和修改字段相当的有用,但是不仅仅是struct字段哦,所有的内置类型和自定义类型都是可以作为匿名字段的。请看下面的例子
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
|
||||
type Skills []string
|
||||
|
||||
type Human struct {
|
||||
name string
|
||||
age int
|
||||
weight int
|
||||
}
|
||||
|
||||
type Student struct {
|
||||
Human // 匿名字段,struct
|
||||
Skills // 匿名字段,自定义的类型string slice
|
||||
int // 内置类型作为匿名字段
|
||||
speciality string
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 初始化学生Jane
|
||||
jane := Student{Human:Human{"Jane", 35, 100}, speciality:"Biology"}
|
||||
// 现在我们来访问相应的字段
|
||||
fmt.Println("Her name is ", jane.name)
|
||||
fmt.Println("Her age is ", jane.age)
|
||||
fmt.Println("Her weight is ", jane.weight)
|
||||
fmt.Println("Her speciality is ", jane.speciality)
|
||||
// 我们来修改他的skill技能字段
|
||||
jane.Skills = []string{"anatomy"}
|
||||
fmt.Println("Her skills are ", jane.Skills)
|
||||
fmt.Println("She acquired two new ones ")
|
||||
jane.Skills = append(jane.Skills, "physics", "golang")
|
||||
fmt.Println("Her skills now are ", jane.Skills)
|
||||
// 修改匿名内置类型字段
|
||||
jane.int = 3
|
||||
fmt.Println("Her preferred number is", jane.int)
|
||||
}
|
||||
|
||||
从上面例子我们看出来struct不仅仅能够将struct作为匿名字段、自定义类型、内置类型都可以作为匿名字段,而且可以在相应的字段上面进行函数操作(如例子中的append)。
|
||||
|
||||
这里有一个问题:如果human里面有一个字段叫做phone,而student也有一个字段叫做phone,那么该怎么办呢?
|
||||
|
||||
Go里面很简单的解决了这个问题,最外层的优先访问,也就是当你通过`student.phone`访问的时候,是访问student里面的字段,而不是human里面的字段。
|
||||
|
||||
这样就允许我们去重载通过匿名字段继承的一些字段,当然如果我们想访问重载后对应匿名类型里面的字段,可以通过匿名字段名来访问。请看下面的例子
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
|
||||
type Human struct {
|
||||
name string
|
||||
age int
|
||||
phone string // Human类型拥有的字段
|
||||
}
|
||||
|
||||
type Employee struct {
|
||||
Human // 匿名字段Human
|
||||
speciality string
|
||||
phone string // 雇员的phone字段
|
||||
}
|
||||
|
||||
func main() {
|
||||
Bob := Employee{Human{"Bob", 34, "777-444-XXXX"}, "Designer", "333-222"}
|
||||
fmt.Println("Bob's work phone is:", Bob.phone)
|
||||
// 如果我们要访问Human的phone字段
|
||||
fmt.Println("Bob's personal phone is:", Bob.Human.phone)
|
||||
}
|
||||
|
||||
|
||||
## links
|
||||
* [目录](<preface.md>)
|
||||
* 上一章: [流程和函数](<02.3.md>)
|
||||
* 下一节: [面向对象](<02.5.md>)
|
||||
# 2.4 struct
|
||||
|
||||
## struct
|
||||
|
||||
We can define new types of containers of other properties or fields in Go just like in other programming languages. For example, we can create a type called `person` to represent a person, with fields name and age. We call this kind of type a `struct`.
|
||||
|
||||
type person struct {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
|
||||
Look how easy it is to define a `struct`!
|
||||
|
||||
There are two fields.
|
||||
|
||||
- `name` is a `string` used to store a person's name.
|
||||
- `age` is a `int` used to store a person's age.
|
||||
|
||||
Let's see how to use it.
|
||||
|
||||
type person struct {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
|
||||
var P person // p is person type
|
||||
|
||||
P.name = "Astaxie" // assign "Astaxie" to the field 'name' of p
|
||||
P.age = 25 // assign 25 to field 'age' of p
|
||||
fmt.Printf("The person's name is %s\n", P.name) // access field 'name' of p
|
||||
|
||||
There are three more ways to define a struct.
|
||||
|
||||
- Assign initial values by order
|
||||
|
||||
P := person{"Tom", 25}
|
||||
|
||||
- Use the format `field:value` to initialize the struct without order
|
||||
|
||||
P := person{age:24, name:"Bob"}
|
||||
|
||||
- Define an anonymous struct, then initialize it
|
||||
|
||||
P := struct{name string; age int}{"Amy",18}
|
||||
|
||||
Let's see a complete example.
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
|
||||
// define a new type
|
||||
type person struct {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
|
||||
// compare the age of two people, then return the older person and differences of age
|
||||
// struct is passed by value
|
||||
func Older(p1, p2 person) (person, int) {
|
||||
if p1.age>p2.age {
|
||||
return p1, p1.age-p2.age
|
||||
}
|
||||
return p2, p2.age-p1.age
|
||||
}
|
||||
|
||||
func main() {
|
||||
var tom person
|
||||
|
||||
// initialization
|
||||
tom.name, tom.age = "Tom", 18
|
||||
|
||||
// initialize two values by format "field:value"
|
||||
bob := person{age:25, name:"Bob"}
|
||||
|
||||
// initialize two values with order
|
||||
paul := person{"Paul", 43}
|
||||
|
||||
tb_Older, tb_diff := Older(tom, bob)
|
||||
tp_Older, tp_diff := Older(tom, paul)
|
||||
bp_Older, bp_diff := Older(bob, paul)
|
||||
|
||||
fmt.Printf("Of %s and %s, %s is older by %d years\n", tom.name, bob.name, tb_Older.name, tb_diff)
|
||||
|
||||
fmt.Printf("Of %s and %s, %s is older by %d years\n", tom.name, paul.name, tp_Older.name, tp_diff)
|
||||
|
||||
fmt.Printf("Of %s and %s, %s is older by %d years\n", bob.name, paul.name, bp_Older.name, bp_diff)
|
||||
}
|
||||
|
||||
### embedded fields in struct
|
||||
|
||||
I've just introduced to you how to define a struct with field names and type. In fact, Go supports fields without names, but with types. We call these embedded fields.
|
||||
|
||||
When the embedded field is a struct, all the fields in that struct will implicitly be the fields in the struct in which it has been embdedded.
|
||||
|
||||
Let's see one example.
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
|
||||
type Human struct {
|
||||
name string
|
||||
age int
|
||||
weight int
|
||||
}
|
||||
|
||||
type Student struct {
|
||||
Human // embedded field, it means Student struct includes all fields that Human has.
|
||||
speciality string
|
||||
}
|
||||
|
||||
func main() {
|
||||
// initialize a student
|
||||
mark := Student{Human{"Mark", 25, 120}, "Computer Science"}
|
||||
|
||||
// access fields
|
||||
fmt.Println("His name is ", mark.name)
|
||||
fmt.Println("His age is ", mark.age)
|
||||
fmt.Println("His weight is ", mark.weight)
|
||||
fmt.Println("His speciality is ", mark.speciality)
|
||||
// modify notes
|
||||
mark.speciality = "AI"
|
||||
fmt.Println("Mark changed his speciality")
|
||||
fmt.Println("His speciality is ", mark.speciality)
|
||||
// modify age
|
||||
fmt.Println("Mark become old")
|
||||
mark.age = 46
|
||||
fmt.Println("His age is", mark.age)
|
||||
// modify weight
|
||||
fmt.Println("Mark is not an athlet anymore")
|
||||
mark.weight += 60
|
||||
fmt.Println("His weight is", mark.weight)
|
||||
}
|
||||
|
||||

|
||||
|
||||
Figure 2.7 Inheritance in Student and Human
|
||||
|
||||
We see that we can access the age and name fields in Student just like we can in Human. This is how embedded fields work. It's very cool, isn't it? Hold on, there's something cooler! You can even use Student to access Human in this embedded field!
|
||||
|
||||
mark.Human = Human{"Marcus", 55, 220}
|
||||
mark.Human.age -= 1
|
||||
|
||||
All the types in Go can be used as embedded fields.
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
|
||||
type Skills []string
|
||||
|
||||
type Human struct {
|
||||
name string
|
||||
age int
|
||||
weight int
|
||||
}
|
||||
|
||||
type Student struct {
|
||||
Human // struct as embedded field
|
||||
Skills // string slice as embedded field
|
||||
int // built-in type as embedded field
|
||||
speciality string
|
||||
}
|
||||
|
||||
func main() {
|
||||
// initialize Student Jane
|
||||
jane := Student{Human:Human{"Jane", 35, 100}, speciality:"Biology"}
|
||||
// access fields
|
||||
fmt.Println("Her name is ", jane.name)
|
||||
fmt.Println("Her age is ", jane.age)
|
||||
fmt.Println("Her weight is ", jane.weight)
|
||||
fmt.Println("Her speciality is ", jane.speciality)
|
||||
// modify value of skill field
|
||||
jane.Skills = []string{"anatomy"}
|
||||
fmt.Println("Her skills are ", jane.Skills)
|
||||
fmt.Println("She acquired two new ones ")
|
||||
jane.Skills = append(jane.Skills, "physics", "golang")
|
||||
fmt.Println("Her skills now are ", jane.Skills)
|
||||
// modify embedded field
|
||||
jane.int = 3
|
||||
fmt.Println("Her preferred number is", jane.int)
|
||||
}
|
||||
|
||||
In the above example, we can see that all types can be embedded fields and we can use functions to operate on them.
|
||||
|
||||
There is one more problem however. If Human has a field called `phone` and Student has a field with same name, what should we do?
|
||||
|
||||
Go use a very simple way to solve it. The outer fields get upper access levels, which means when you access `student.phone`, we will get the field called phone in student, not the one in the Human struct. This feature can be simply seen as field `overload`ing.
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
|
||||
type Human struct {
|
||||
name string
|
||||
age int
|
||||
phone string // Human has phone field
|
||||
}
|
||||
|
||||
type Employee struct {
|
||||
Human // embedded field Human
|
||||
speciality string
|
||||
phone string // phone in employee
|
||||
}
|
||||
|
||||
func main() {
|
||||
Bob := Employee{Human{"Bob", 34, "777-444-XXXX"}, "Designer", "333-222"}
|
||||
fmt.Println("Bob's work phone is:", Bob.phone)
|
||||
// access phone field in Human
|
||||
fmt.Println("Bob's personal phone is:", Bob.Human.phone)
|
||||
}
|
||||
|
||||
## Links
|
||||
|
||||
- [Directory](preface.md)
|
||||
- Previous section: [Control statements and functions](02.3.md)
|
||||
- Next section: [Object-oriented](02.5.md)
|
||||
|
||||
Reference in New Issue
Block a user