一、结构体
结构体可以看做是一堆数据的结合。
声明结构体:
type Person struct {Name stringAge intSex int}
创建结构体:第一种方式
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}
}
接口的实现条件:
- 接口的方法与实现接口的类型方法格式一致
- 接口中所有方法均被实现
:::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())
}
