Go语言中的面向对象编程技巧
发表时间: 2023-10-20 17:36
对于面向对象编程的Go语言设计得非常简洁而优雅。 因为, Go语言并没有沿袭传统面向对象编程中的诸多概念, 比如继承(不支持继承, 尽管匿名字段的内存布局和行为类似继承, 但它并不是继承)、
虚函数、构造函数和析构函数、隐藏的this指针等。
尽管Go语言中没有封装、继承、多态这些概念, 但同样通过别的方式实现这些特性:
封装: 通过方法实现
继承: 通过匿名字段实现
多态: 通过接口实现
匿名组合
匿名字段
一般情况下, 定义结构体的时候是字段名与其类型一一对应, 实际上Go支持只提供类型, 而不写字段名的方式, 也就是匿名字段, 也称为嵌入字段。
当匿名字段也是由一个结构体的时候, 那么这个结构体所拥有的全部字段都被隐式地引入了当前定义的这个结构体。
//人
type Person struct {
name string
sex byte
age int
}
//学生
type Student struct {
Person //匿名字段, 那么默认Student就包含了Person的所有字段
id int
addr string
}
初始化
package main //必须有个main包import "fmt"//人type Person struct { name string sex byte age int}//学生type Student struct { Person //匿名字段, 那么默认Student就包含了Person的所有字段 id int addr string}func main(){ //顺序初始化 var s1 Student = Student{Person{"mike", 'm', 18}, 1, "sz"} fmt.Println("s1 = ", s1) //s1 = {{mike 109 18} 1 sz} //自动推导类型 s2 := Student{Person{"mike", 'm', 18}, 1, "sz"} fmt.Println("s2 = ", s2) //s2 = {{mike 109 18} 1 sz} //%+v, 显示更详细 fmt.Printf("s2 = %+v\n", s2) //s2 = {Person:{name:mike sex:109 age:18} id:1 addr:sz} //部分成员初始化, 没有初始化的自动赋值为0 s3 := Student{Person: Person{"lily", 'f', 19}, id:2} fmt.Printf("s3 = %+v\n", s3) //s2 = {Person:{name:mike sex:109 age:18} id:1 addr:sz} s4 := Student{Person: Person{name: "tom"}, id:4} fmt.Printf("s4 = %+v\n", s4) //s4 = {Person:{name:tom sex:0 age:0} id:4 addr:}}
成员操作
package main //必须有个main包import "fmt"//人type Person struct { name string sex byte age int}//学生type Student struct { Person //匿名字段, 那么默认Student就包含了Person的所有字段 id int addr string}func main(){ var s1 Student //变量声明 //给成员赋值 s1.name = "mike" //等价于s1.Person.name = "mike" s1.sex = 'm' s1.age = 18 s1.id = 1 s1.addr = "sz" fmt.Printf("s1 = %+v\n", s1) //s1 = {Person:{name:mike sex:109 age:18} id:1 addr:sz} var s2 Student //变量声明 s2.Person = Person{"lily", 'f', 19} s2.id = 2 s2.addr = "bj" fmt.Printf("s2 = %+v\n", s2) //s2 = {Person:{name:lily sex:102 age:19} id:2 addr:bj}}
同名字段
package main //必须有个main包import "fmt"//人type Person struct { name string sex byte age int}//学生type Student struct { Person //匿名字段, 那么默认Student就包含了Person的所有字段 id int addr string name string //和Person中的name同名}func main(){ var s Student s.name = "mike" fmt.Printf("s = %+v\n", s) //s = {Person:{name: sex:0 age:0} id:0 addr: name:mike} //默认只会给最外层的成员赋值 //给匿名同名成员赋值, 需要显示调用 s.Person.name = "yoyo" fmt.Printf("s = %+v\n", s) //s = {Person:{name:yoyo sex:0 age:0} id:0 addr: name:mike}}
其他匿名字段
非结构体类型
所有的内置类型和自定义类型都是可以作为匿名字段的:
package main //必须有个main包import "fmt"type mystr string //自定义类型//人type Person struct { name string sex byte age int}//学生type Student struct { Person //匿名字段, 那么默认Student就包含了Person的所有字段 int //匿名字段, 内置类型 mystr //匿名字段, 自定义类型}func main(){ //初始化 s1 := Student{Person{"mike", 'm', 18}, 1, "bj"} fmt.Printf("s1 = %+v\n", s1) //s1 = {Person:{name:mike sex:109 age:18} int:1 mystr:bj} //成员操作 fmt.Printf("%s, %c, %d, %d, %s\n", s1.name, s1.sex, s1.age, s1.int, s1.mystr) //mike, m, 18, 1, bj fmt.Printf("%s, %c, %d, %d, %s\n", s1.Person.name, s1.Person.sex, s1.Person.age, s1.int, s1.mystr) //mike, m, 18, 1, bj}
结构体指针类型
package main //必须有个main包import "fmt"//人type Person struct { name string sex byte age int}//学生type Student struct { *Person //匿名字段,结构体指针类型 id int addr string}func main(){ //初始化 s1 := Student{&Person{"mike", 'm', 18}, 1, "bj"} fmt.Printf("s1 = %+v\n", s1) //s1 = {Person:0xc000044400 id:1 addr:bj} fmt.Printf("%s, %c, %d, %d\n", s1.name, s1.sex, s1.age, s1.Person.age) //mike, m, 18 18 //声明变量 var s2 Student s2.Person = new(Person) //是指针类型, 必须分配空间 s2.name = "yoyo" s2.sex = 'f' s2.age = 20 s2.id = 2 s2.addr = "sz" fmt.Println(s2.name, s2.sex, s2.age, s2.id, s2.age) //yoyo 102 20 2 20}