封装
通常是指将数据(字段)和操作这些数据的功能(方法)组合在一起,同时控制数据的访问权限。
字段大小写
方法大小写
package main
import "fmt"
type Person struct {
name string // 私有字段
Age int // 公有字段
}
// 通过指针传递结构体,能够修改字段
func (p *Person) SetName(name string) {
p.name = name // 通过指针修改私有字段
}
// 通过指针传递结构体,能够修改字段
func (p *Person) SetAge(age int) {
p.Age = age // 通过指针修改公有字段
}
// 通过值传递结构体,只能读取数据,不能修改
func (p Person) GetName() string {
return p.name // 读取私有字段
}
// 通过值传递结构体,读取公有字段
func (p Person) GetAge() int {
return p.Age // 读取公有字段
}
func main() {
// 创建一个 Person 实例
p := Person{Age: 25}
// 通过方法修改公有字段和私有字段
p.SetAge(30) // 直接通过方法修改公有字段
p.SetName("Alice") // 直接通过方法修改私有字段
// 通过方法获取字段值
fmt.Println("Name:", p.GetName()) // 输出:Name: Alice
fmt.Println("Age:", p.GetAge()) // 输出:Age: 30
// 注意,直接访问私有字段是非法的
// fmt.Println(p.name) // 编译错误:p.name undefined (cannot refer to unexported field or method name)
// 通过指针修改字段
pPointer := &p
pPointer.SetAge(35)
fmt.Println("Age after pointer modification:", p.GetAge()) // 输出:Age after pointer modification: 35
// 通过值传递不会修改原数据
newP := p
newP.SetAge(40)
fmt.Println("Age after value modification:", p.GetAge()) // 输出:Age after value modification: 35
}
继承
隐式:golang里面的继承、不需要再跟结构体一样把接口嵌套进去、只需要实现接口里面的所有方法就可以了。
结构体继承
// 通过嵌套结构体、来实现继承、
// 1.嵌套了父结构体的子结构体可以使用父结构体的方法
package main
import "fmt"
// 定义一个父结构体(可以理解为“父类”)
type Animal struct {
Name string
}
// 父结构体的方法
func (a *Animal) Speak() {
fmt.Println(a.Name + " makes a sound.")
}
// 定义子结构体(可以理解为“子类”)
type Dog struct {
Animal // 这里嵌入了父结构体Animal
}
// 主函数
func main() {
// 创建 Dog 对象,初始化时嵌入了 Animal 对象
dog := Dog{Animal{Name: "Buddy"}}
// 调用 Dog 的 Speak 方法(没有重写父类方法)
dog.Speak() // 输出:Buddy makes a sound.
}
// 2.嵌套了父结构体的子结构体也重写父结构体的方法
package main
import "fmt"
// 定义一个父结构体(可以理解为“父类”)
type Animal struct {
Name string
}
// 父结构体的一个方法
func (a *Animal) Speak() {
fmt.Println(a.Name + " makes a sound.")
}
// 定义子结构体(可以理解为“子类”)
type Dog struct {
Animal // 这里嵌入了父结构体Animal
}
// 子结构体的一个方法,重写了父结构体的Speak方法
func (d *Dog) Speak() {
fmt.Println(d.Name + " barks.")
}
func main() {
// 创建Dog对象,初始化时嵌入了Animal对象
dog := Dog{Animal{Name: "Buddy"}}
dog.Speak() // 输出:Buddy barks.
dog.Animal.Speak() // 输出:Buddy makes a sound.
}
接口继承
接口嵌套:通过在一个接口中嵌套其他接口,来继承这些接口的所有方法。
隐式实现:Go 的类型通过实现接口的所有方法来“自动”实现该接口,不需要显式声明实现接口。
package main
import "fmt"
// 定义一个接口 Animal
type Animal interface {
Speak() string
}
// 定义一个接口 Mammal,继承 Animal 接口
type Mammal interface {
Animal // 嵌套 Animal 接口
Move() string
}
// 定义一个结构体 Dog
type Dog struct{}
// Dog 实现了 Animal 接口的 Speak 方法
func (d Dog) Speak() string {
return "Woof!"
}
// Dog 实现了 Mammal 接口的 Move 方法
func (d Dog) Move() string {
return "Dog moves on four legs."
}
func main() {
// 创建 Dog 对象
dog := Dog{}
// 使用 Mammal 接口来引用 Dog
var mammal Mammal = dog
fmt.Println(mammal.Speak()) // 输出:Woof!
fmt.Println(mammal.Move()) // 输出:Dog moves on four legs.
}
// 这里dog不仅实现了Mammal接口、也实现了Animal接口、因此也可以使用Animal接口的方法、例子如下
var a Animal = dog{}
fmt.Print(a.Speak()) 多态
多态就是不同结构体可以通过一个接口实现不同的行为
有一个父类(接口)
有子类、实现了父类的全部方法
例子1
package main
import "fmt"
// 定义一个接口
type Speaker interface {
Speak() string
}
// Dog 类型实现了 Speaker 接口
type Dog struct{}
func (d Dog) Speak() string {
return "Woof!"
}
// Cat 类型实现了 Speaker 接口
type Cat struct{}
func (c Cat) Speak() string {
return "Meow!"
}
// 函数接收一个 Speaker 接口类型的参数
func Introduce(speaker Speaker) {
fmt.Println(speaker.Speak())
}
func main() {
d := Dog{}
c := Cat{}
Introduce(d) // 输出:Woof!
Introduce(c) // 输出:Meow!
}
例子2
package main
import "fmt"
// 定义一个接口
type Shape interface {
Area() float64
}
// 定义 Rectangle 类型
type Rectangle struct {
Width, Height float64
}
// 实现 Shape 接口的 Area 方法
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// 定义 Circle 类型
type Circle struct {
Radius float64
}
// 实现 Shape 接口的 Area 方法
func (c Circle) Area() float64 {
return 3.14 * c.Radius * c.Radius
}
// 打印形状的面积
func PrintArea(s Shape) {
fmt.Println("Area:", s.Area())
}
func main() {
r := Rectangle{Width: 5, Height: 3}
c := Circle{Radius: 4}
// 通过接口统一处理不同类型
PrintArea(r) // 输出:Area: 15
PrintArea(c) // 输出:Area: 50.24
}Rectangle和Circle类型分别实现了Shape接口的Area()方法。PrintArea函数接收一个Shape类型的参数,通过调用接口的方法Area()来统一计算不同形状的面积。虽然
r和c是不同的类型,但它们都通过接口Shape被统一处理,这就是多态的表现。
前面的例子使用的都是值、不能修改结构体的字段、如果要修改结构体的字段、需要使用指针
package main
import "fmt"
// 定义一个接口
type Speaker interface {
Speak() string
}
// Dog 类型实现了 Speaker 接口,方法接收者是指针类型
type Dog struct {
name string
}
func (d *Dog) Speak() string {
return "Woof! My name is " + d.name
}
// 函数接收一个 Speaker 接口类型的参数
func Introduce(speaker Speaker) {
fmt.Println(speaker.Speak())
}
func main() {
d := &Dog{name: "Rex"}
Introduce(d) // 输出:Woof! My name is Rex
}