TOC

Golang 结构体

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
)

type Address struct {
    City    string `json:"city"`
    Country string `json:"country"`
}

type User struct {
    Username string `json:"username"`
    Password string `json:"password"`
}

type Person struct {
    Name    string  `json:"name"`
    Age     int     `json:"age"`
    Address Address `json:"address"`
    // 这么写,JSON 会多一层
    // User    `json:"user"`
    User
}

func (p Person) SayHello() {
    fmt.Println("Hello, my name is", p.Name)
}

func main() {
    person := Person{
        Name: "Bob",
        Age:  30,
        Address: Address{
            City:    "London",
            Country: "UK",
        },
        User: User{
            Username: "bob1999",
            Password: "pa55w0rd",
        },
    }

    // 属性操作
    fmt.Println("Name:", person.Name)
    fmt.Println("Age:", person.Age)
    fmt.Println("City:", person.Address.City)
    fmt.Println("Country:", person.Address.Country)
    fmt.Println("Username:", person.Username)
    fmt.Println("Password:", person.Password)
    fmt.Printf("%+v\n", person)
    fmt.Printf("%#v\n", person)

    // 方法调用
    person.SayHello()

    // 通过反射获取标签内容
    t := reflect.TypeOf(person)
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        tag := field.Tag.Get("json")
        fmt.Println(field.Name, ":", tag)
    }

    // JSON 序列化
    jsonData, err := json.Marshal(person)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println("JSON:", string(jsonData))
}

// Name: Bob
// Age: 30
// City: London
// Country: UK
// Username: bob1999
// Password: pa55w0rd
// main.Person{name:"Bob", age:30, address:main.Address{city:"London", country:"UK"}, User:main.User{username:"bob1999", password:"pa55w0rd"}}

其他知识点:

  1. 字段名大小写问题:首字母小写的话,只有本包可以访问

  2. Zero-value Struct

    type Fruit struct {
        name string
    }
    var apple Fruit
    fmt.Println(apple) // {}
    
  3. new 关键字

    package main
    
    import "fmt"
    
    type Employee struct {
        Name string
        Age  int
    }
    
    func main() {
        p := new(Employee)
        fmt.Println(p) // &{ 0}
    
        var p2 Employee
        fmt.Println(p2) // { 0}
    
        p3 := Employee{"Me", 30}
        fmt.Println(p3) // {Me 30}
    }
    
  4. struct 字面量

    type Person struct {
        Name  string
        Age   int
        Email string
    }
    
    p1 := Person{
        Name:  "Alice",
        Age:   25,
        Email: "alice@example.com",
    }
    p2 := Person {"Alice", 25, "alice@example.com"}
    
  5. 指针

  6. 匿名结构体

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        user := struct {
            username string
        }{
            username: "admin",
        }
        fmt.Printf("%+v\n", user)
        // {username:admin}
        fmt.Printf("%#v\n", user)
        // struct { username string }{username:"admin"}
        fmt.Printf("%v\n", user)
        // {admin}
        fmt.Println(user)
        // {admin}
    }
    

    场景:

    1. 定义一个临时结构体接收反序列化数据
    2. 内嵌结构体
  7. 匿名属性(或者应该叫啥,我也不知道)

    1. 内嵌类型不能重复,
    2. 前面的例子可以看出,可以直接访问匿名属性(结构体)内部属性,
    3. 但是如果同名就只能老老实实通过类型名字来访问了
    package main
    
    import (
        "fmt"
    )
    
    type Book struct {
        string // book name (anno field)
    
        // syntax error: unexpected [, expected field name or embedded type
        // []Author
        Authors []Author // slice of authors
    
        Publisher
    }
    
    type Author struct {
        string // author name (anno field)
    }
    
    type Publisher struct {
        string // publisher name (anno field)
    }
    
    func main() {
        p := Book{
            string: "Learning Python",
            Authors: []Author{
                {string: "Stanley B.Lippman"},
                {string: "Josée LaJoie"},
                {"Barbara E.Moo"},
            },
            Publisher: Publisher{"人民邮电出版社"},
        }
        p.string = "C++ Primer" // change attr
    
        fmt.Println(p)
        fmt.Printf("%v\n", p)
        fmt.Printf("%+v\n", p)
        fmt.Printf("%#v\n", p)
        fmt.Printf("%v\n", p.string)
        fmt.Printf("%v\n", p.Authors[0].string)
        fmt.Printf("%v\n", p.Publisher)
        fmt.Printf("%v\n", p.Publisher.string)
    }
    
    // {C++ Primer [{Stanley B.Lippman} {Josée LaJoie} {Barbara E.Moo}] {人民邮电出版社}}
    // {C++ Primer [{Stanley B.Lippman} {Josée LaJoie} {Barbara E.Moo}] {人民邮电出版社}}
    // {string:C++ Primer Authors:[{string:Stanley B.Lippman} {string:Josée LaJoie} {string:Barbara E.Moo}] Publisher:{string:人民邮电出版社}}
    // main.Book{string:"C++ Primer", Authors:[]main.Author{main.Author{string:"Stanley B.Lippman"}, main.Author{string:"Josée LaJoie"}, main.Author{string:"Barbara E.Moo"}}, Publisher:main.Publisher{string:"人民邮电出版社"}}
    // C++ Primer
    // Stanley B.Lippman
    // {人民邮电出版社}
    // 人民邮电出版社
    
  8. 将函数通过属性的方式定义

    type FoodNameGetter func(string) string
    
    type Food struct {
        name   string
        getter FoodNameGetter // declare function
    }
    
    pizza := Food{
        name: "Pizza",
        getter: func(name string) string { // declare function body
            return "This is " + name + "."
        },
    }
    
  9. 结构体比较

    如果类型和值相同,就等于。

    package main
    
    import "fmt"
    
    type Teacher struct {
        name string
    }
    
    type Student struct {
        name string
    }
    
    func main() {
        s1 := Student{"John"}
        s2 := Student{"John"}
        fmt.Println(s1 == s2) // true
    
        // invalid operation: s1 == t1 (mismatched types Student and Teacher)
        // t1 := Teacher{"John"}
        // fmt.Println(s1 == t1)
    }
    

参考资料与拓展阅读