一、结构体

结构体可以看做是一堆数据的结合。

声明结构体:

  1. type Person struct {
  2. Name string
  3. Age int
  4. Sex int
  5. }

创建结构体:
第一种方式

person := Person{Name: "xiaoyu", Age: 18, Sex: 1}
fmt.Println(person)

// or
person := Person{"xiaoyu", 18, 1}
fmt.Println(person) // {xiaoyu 18 1}

// or
var person = Person{Name: "xiaoyu", Age: 18, Sex: 1}
fmt.Println(person) // {xiaoyu 18 1}

第二种方式

var person Person
person.Name = "xiaoyu"
person.Age = 18
person.Sex = 1
fmt.Println(person) // {xiaoyu 18 1}

第三种方式:使用new方法,详见📃 内建方法

person := new(Person)
person.Name = "xiaoyu"
person.Age = 18
person.Sex = 1
fmt.Println(person) // &{xiaoyu 18 1}

结构体的方法

go语言中结构体的方法看起来像是在JS的原型中添加方法:

func main() {
    person := Person{Name: "Xiaoyu", Age: 18, Sex: 1}
    person.Say("hello") // Xiaoyu say hello
}

type Person struct {
    Name string
    Age int
    Sex int
}

func (person *Person) Say(words string) {
    fmt.Println(person.Name, "say", words)
}

尝试将Say换为say,发现会报错。go就是通过控制方法的大小写来控制其作用域,如果是小写只能内部调用。

使用组合模拟类的继承

在go中,通过组合的方式模拟面向对象中的 继承

func main() {
    person := Person{Name: "Xiaoyu", Age: 18, Sex: 1}
    person.Kind = "mammalia"
    person.Eat() // 好吃
    fmt.Println(person) // {{mammalia} Xiaoyu 18 1}
}

type Animal struct {
    Kind string
}

func (person *Animal) Eat() {
    fmt.Println("好吃")
}

type Person struct {
    Animal // 继承
    Name string
    Age int
    Sex int
}

func (person *Person) Say(words string) {
    fmt.Println(person.Name, "say", words)
}

内嵌结构中的命名冲突

如果一个结构体组合了多个结构体,而这些结构体中包含相同的字段名,那么就不能直接为这个字段赋值了,需要先指定是哪个结构体中的字段。

type A struct {
    a int
}

type B struct {
    a int
}

type C struct {
    A
    B
}

func main() {
    c := &C{}
    c.A.a = 1 // 指定为A中的a字段
    c.B.a = 2 // 指定为B中的a字段
    fmt.Println(c) // &{{1} {2}}
}

匿名结构体

跟匿名函数一样,结构体也可以匿名化:

    msg := &struct {
        status   int
        data string
    }{ 200, "ok"}

    fmt.Println(msg)

树状结构定义

使用指针对结构体自引用,可定义一个树状结构的结构体:

func main() {
    person := &Person{
        name: "Job Smith",
        children: []*Person{
            {
                name: "Bob Smith",
                children: []*Person{
                    {
                        name: "Joy Smith",
                    },
                },
            },
            {
                name: "Bob Smith",
            },
        },
    }

    fmt.Println(person.children[0].name) // Bob Smith
}

type Person struct {
    name  string
    children []*Person
}

构造函数

go语言结构体的构造函数可以用一个 返回结构体自身类型的指针 函数进行模拟:

func main() {
    person := NewPerson("xiaoyu", 18, 1)
    fmt.Println(person)
}

type Person struct {
    Name string
    Age int
    Sex int
}

func NewPerson(name string, age int, sex int) *Person {
    return &Person{name, age, sex}
}

二、接口

定义一个接口:

type Behavior interface {
    Eat() string
    Run() string
}

实现接口

在go中不需要像Java一样使用implements关键字来实现接口,只需要保证struct中的方法、并传入的参数类型、数量、返回值类型一致即可:


type Animal interface {
    Grow()
    Move(string) string
}

type Cat struct {
    Name string
    Age int
    Place string
}

func (cat *Cat) Grow() {

}

func (cat *Cat) Move(str string) string {
    return ""
}

func main() {
    cat := Cat{"Kitty", 2, "House"}
    animal, ok := interface{}(&cat).(Animal)
    fmt.Printf("%v, %v \n", ok, animal) // true, &{Kitty 2 House}
}

接口的实现条件:

  1. 接口的方法与实现接口的类型方法格式一致
  2. 接口中所有方法均被实现

:::info Go语言的接口实现是隐式的,无须让实现接口的类型写出实现了哪些接口。这个设计被称为非侵入式设计。 :::

通过接口实现多态

func main() {
    dog := Dog{Name: "wangwang", Age: 2}
    cat := Cat{Name: "mimi", Age: 2}
    fmt.Println(dog.Eat()) // dog: eat
    fmt.Println(dog.Run()) // dog: run
    fmt.Println(cat.Eat()) // cat: eat
    fmt.Println(cat.Run()) // cat: run
}

type Animal struct {
    Kind string
}

type Cat struct {
    Animal
    Name string
    Age int
}

func (cat *Cat) Eat() string {
    return "cat: eat"
}

func (cat *Cat) Run() string {
    return "cat: run"
}

type Dog struct {
    Animal
    Name string
    Age int
}

func (dog *Dog) Eat() string {
    return "dog: eat"
}

func (cat *Dog) Run() string {
    return "dog: run"
}

可以先声明一个接口类型的变量,然后通过 new 方法创建对应类型的实例:

var b1 Behavior
b1 = new(Dog)
fmt.Println(b1.Eat())
fmt.Println(b1.Run())

var b2 Behavior
b2 = new(Cat)
fmt.Println(b2.Eat())
fmt.Println(b2.Run())

接口还可以当做方法的参数:

func main() {
    var dog = new(Dog)
    action(dog)

    var cat = new(Cat)
    action(cat)
}

func action(b Behavior) {
    fmt.Println(b.Eat())
    fmt.Println(b.Run())
}