《Structures in Go (structs)》学习笔记
《Anatomy of methods in Go》学习笔记
**

结构体

结构体 VS 结构体类型

  • 结构体类型

    1. type StructName struct {
    2. field1 fieldType1
    3. field2 fieldType2
    4. }

    **

  • 结构体

结构体类型的实现

结构体的zero值

  • string 类型的默认值是“”,并不打印
  • int 类型的默认值是0
  • bool 类型的默认值是false ```go package main import “fmt”

func main() { type Employee struct { firstName, lastName string salary int fullTiem bool }

  1. var boss Employee
  2. fmt.Println(boss) // { 0 false }

}

  1. **
  2. <a name="Zbbdq"></a>
  3. ### 实例结构类型
  4. - 普通方式
  5. ```go
  6. type Employee struct {
  7. firstName, lastName string
  8. salary int
  9. fullTime bool
  10. }
  11. ross := Employee{
  12. firstName: "Yang",
  13. lastName: "Yang",
  14. }

**

  • 匿名方式
  1. ross := struct {
  2. firstName, lastName string
  3. salary int
  4. fullTime bool
  5. } {
  6. firstName: "Yang",
  7. lastName: "Yang",
  8. salary: 200,
  9. fullTime true,
  10. }

结构体指针

  1. type Employee struct {
  2. firstName, lastName string
  3. salary int
  4. fullTime bool
  5. }
  6. ross := &Employee{
  7. firstName: "Yang",
  8. lastName: "YY",
  9. salary: 1200,
  10. fullTime: false,
  11. }
  12. ross.firstName
  13. (*ross).firstName

对于结构体指针,golang可以通过指针或直接访问内部属性

匿名结构体属性

  1. type Data struct {
  2. string
  3. int
  4. bool
  5. }
  6. sample1 := Data{"Monday", 1200, true}

嵌套结构体与嵌套结构体属性上移

  • 若嵌套的结构体作为父结构体的匿名属性,嵌套结构体内的属性会自动上升到父结构体上
  • 若父结构体已有属性与嵌套结构体内的属性重名则以父结构体内属性为准
  1. type Salary struct {
  2. basic int
  3. insurance int
  4. allowance int
  5. }
  6. type Employee struct {
  7. firstName, lastName string
  8. Salary
  9. }

接口作为结构体属性

在接口中定义的方法,一旦结构体实现该接口中的方法则说明结构体实现了该接口
_

将接口定义为结构体的属性,所有实现该接口的子结构体都可以赋值给该属性

  1. package main
  2. import "fmt"
  3. type Salaried interface {
  4. getSalary() int
  5. }
  6. type Salary struct {
  7. basic int
  8. insurance int
  9. allowance int
  10. }
  11. func (s Salary) getSalary() int {
  12. return s.basic + s.insurance + s.allowance
  13. }
  14. type Employee struct {
  15. firstName, lastName string
  16. salary Salaried
  17. }
  18. func main() {
  19. ross := Employee{
  20. firstName: "YY",
  21. lastName: "Yang",
  22. salary: Salary{10, 20, 30},
  23. }
  24. fmt.Println("Ross's salary is ", ross.salary.getSalary())
  25. }

接口作为结构体匿名的属性,实现接口的子结构体中相关的接口方法将会上升到父结构体的属性上

需要特别注意的是实现接口的子结构体接口方法可以上升,但是子结构体内的其它属性不能上升。因为子结构体不是父结构体的内嵌结构体

  • 静态类型:变量声明时的类型
  • 动态类型:具体类型(即变量在实际执行时的类型)
  • 当执行结构体中接口类型的属性上的接口方法时,实际执行的是该接口的动态方法
  1. type Salaried interface {
  2. getSalary() int
  3. }
  4. type Salary struct {
  5. basic int
  6. insurance int
  7. allowance int
  8. }
  9. func (s Salary) getSalary() int {
  10. return s.basic + s.insurance + s.allowance
  11. }
  12. type Employee struct {
  13. firstName, lastName string
  14. Salaried
  15. }
  16. func main() {
  17. ross := Employee{
  18. firstName: "YY",
  19. lastName: "Yang",
  20. Salaried: Salary{10, 20, 30},
  21. }
  22. fmt.Println("Ross's salary is ", ross.getSalary())
  23. }

函数作为结构体属性

golang中函数可以作为数据类型

创建别名为 fullNameType 的函数类型

  1. package main
  2. import "fmt"
  3. type fullNameType func(string, string) string
  4. type Employee struct {
  5. firstName, lastName string
  6. fullName fullNameType
  7. }
  8. func main() {
  9. e := Employee{
  10. firstName: "YY",
  11. lastName: "Yang",
  12. fullName: func(firstName string, lastName string) string {
  13. return firstName + " " + lastName
  14. },
  15. }
  16. fmt.Println(e.fullName(e.firstName, e.lastName))
  17. }

_
为什么 e.fullName(e.firstName, e.lastName) 使用 e.firstName 和 e.lastName?
可参照method方法

方法(Method)

方法 VS 函数

定义

方法和函数的区别在于方法归属明确的结构体类型

  • 方法
  1. func (r Type) functionName(...Type) Type {
  2. }
  • 函数
  1. func functionName(...Type) Type {
  2. }

使用结构体函数属性模拟方法

可以在结构体中定义函数属性实现在结构体中定义方法相同的功能

关于指针的使用

  • 函数的函数若定义成指针,则只能接收指针、若定义成普通值,则只能接收普通值
  • 方法的接收者若定义成指针,可以使用地址或普通值调用,反则一样

同名方法

  • 在包内可以指定多个同名的方法,但是方法指定的接受者类型要不同
  • 在包内不可以指定多个同名函

方法的接收是指针

  • 如果方法的接收类型是指针,在方法中你可以使用(*e).x 也可以直接使用e.x获取结构体属性的值

golang会自动将普通值转换为指针值

  1. func (e *Employee) changeName(newName string) {
  2. e.name = newName
  3. }

等价于

  1. func (e *Employee) changeName(newName string) {
  2. (*e).name = newName
  3. }
  • 如果方法的接收类型是指针,调用方法的可以使普通类型也可以是地址
  1. package main
  2. import "fmt"
  3. type Employee struct {
  4. name string
  5. salary int
  6. }
  7. func (e *Employee) changeName(newName string) {
  8. e.name = newName
  9. }
  10. func main() {
  11. e := Employee{
  12. name: "YY",
  13. salary: 20,
  14. }
  15. fmt.Printf("The origin value %v\n", e)
  16. e.changeName("Spursyy")
  17. fmt.Printf("After change name %v", e)
  18. }

方法的接收者

  • 无论定义方法的接收是指针还是普通类型,都是使用地址类型或者普通类型调用方法
  • 如果方法的接收者是普通类型,无论是地址类型还是普通类型调用该方法,传递给该方法都只是一个值的拷贝
  • 如果方法的接收者是指针类型,无论是地址类型还是普通类型调用该方法,传递给该方法都只是一个值的引用

如果为其它包的结构体定义方法

  • 如果结构体和结构体定义的方法不在同一个包内,则需要在该包内定义一个结构体类型别名
  • 然后别名结构体定义方法
  1. package main
  2. import (
  3. "fmt"
  4. "strings"
  5. )
  6. type MyString string
  7. func (s MyString)toUpperCase() string {
  8. normalString := string(s)
  9. return strings.ToUpper(normalString)
  10. }
  11. func main() {
  12. str := MyString("Hello World")
  13. fmt.Println(str.toUpperCase())
  14. }